question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Async process inside success event

See original GitHub issue

I’m having some trouble leveraging rule-chaining to add some runtime facts based on facts already in the Almanac like described here https://github.com/CacheControl/json-rules-engine/blob/master/docs/almanac.md#retrieve-fact-values-when-handling-events

I’ve modified https://github.com/CacheControl/json-rules-engine/blob/master/examples/07-rule-chaining.js to provide an primitive example of what I’m trying to do

/*
 * This is an advanced example demonstrating rules that passed based off the
 * results of other rules by adding runtime facts.  It also demonstrates
 * accessing the runtime facts after engine execution.
 *
 * Usage:
 *   node ./examples/07-rule-chaining.js
 *
 * For detailed output:
 *   DEBUG=json-rules-engine node ./examples/07-rule-chaining.js
 */

require('colors');
const { Engine } = require('../dist').Engine

/**
 * Setup a new engine
 */
const engine = new Engine();

/**
 * Rule for identifying people who may like screwdrivers
 */
const drinkRule = {
  conditions: {
    all: [
      {
        fact: 'drinksOrangeJuice',
        operator: 'equal',
        value: true,
      },
      {
        fact: 'enjoysVodka',
        operator: 'equal',
        value: true,
      },
    ],
  },
  event: { type: 'drinks-screwdrivers' },
  priority: 10, // IMPORTANT!  Set a higher priority for the drinkRule, so it runs first
};
engine.addRule(drinkRule);

/**
 * Register listeners with the engine for rule success and failure
 */
let facts;
engine
  .on('success', async (event, almanac) => {
    console.log(`${facts.accountId + ' DID '.green}meet conditions for the ${event.type.underline} rule.`);
    return new Promise((resolve) => {
      setTimeout(() => {
        console.log('promise resolved');
        resolve();
      }, 0);
    });
  })
  .on('failure', (event) => {
    console.log(`${facts.accountId} did ${'NOT'.red} meet conditions for the ${event.type.underline} rule.`);
  });

// define fact(s) known at runtime
facts = {
  accountId: 'washington',
  drinksOrangeJuice: true,
  enjoysVodka: true,
  isSociable: true,
};
engine
  .run(facts) // first run, using washington's facts
  .then(() => {
    console.log('engine run completed');
  })
  .catch();

/*
 * OUTPUT:
 *
 * washington DID meet conditions for the drinks-screwdrivers rule.
 * engine run completed
 * promise resolved
 */

I would expect the success event to complete before the engine run finishes like so:

/*
 * OUTPUT:
 *
 * washington DID meet conditions for the drinks-screwdrivers rule.
 * promise resolved
 * engine run completed
 */

Here’s a more concrete example based on my use case. I’m reading some facts from the Almanac, and I plan to assign a runtime fact based on those values.

const { Engine } = require('../dist').Engine

/**
 * Setup a new engine
 */
require('colors');

const engine = new Engine();

/**
 * Rule for identifying people who may like screwdrivers
 */
const drinkRule = {
  conditions: {
    all: [
      {
        fact: 'drinksOrangeJuice',
        operator: 'equal',
        value: true,
      },
      {
        fact: 'enjoysVodka',
        operator: 'equal',
        value: true,
      },
    ],
  },
  event: { type: 'drinks-screwdrivers' },
  priority: 10, // IMPORTANT!  Set a higher priority for the drinkRule, so it runs first
};
engine.addRule(drinkRule);

/**
 * Register listeners with the engine for rule success and failure
 */
let facts;
engine
  .on('success', async (event, almanac) => {
    almanac.addRuntimeFact('screwdriverAficionado', true);
    console.log(`${facts.accountId + ' DID '.green}meet conditions for the ${event.type.underline} rule.`);
    let someInput;
    someInput = await almanac.factValue('someInput');
    someInput = await almanac.factValue('someInput');
    someInput = await almanac.factValue('someInput');
    someInput = await almanac.factValue('someInput');
    someInput = await almanac.factValue('someInput');
    someInput = await almanac.factValue('someInput');
    someInput = await almanac.factValue('someInput');
    someInput = await almanac.factValue('someInput');
    someInput = await almanac.factValue('someInput');
    almanac.addRuntimeFact(`newFact`, someInput);
    console.log('resolved input', someInput);
  })
  .on('failure', (event) => {
    console.log(`${facts.accountId} did ${'NOT'.red} meet conditions for the ${event.type.underline} rule.`);
  });

// define fact(s) known at runtime
facts = {
  accountId: 'washington',
  drinksOrangeJuice: true,
  enjoysVodka: true,
  isSociable: true,
  someInput: [1, 2, 3],
};
engine
  .run(facts)
  .then((results) => {
    console.log('engine run completed');
    // console.log(results);
  })
  .catch();

/*
 * OUTPUT:
 *
 * washington DID meet conditions for the drinks-screwdrivers rule.
 * engine run completed
 * resolved input [ 1, 2, 3 ]
 */

And if I remove some of those async processes, then I get my expected behavior

...
engine
  .on('success', async (event, almanac) => {
    almanac.addRuntimeFact('screwdriverAficionado', true);
    console.log(`${facts.accountId + ' DID '.green}meet conditions for the ${event.type.underline} rule.`);
    let someInput;
    someInput = await almanac.factValue('someInput');
    almanac.addRuntimeFact(`newFact`, someInput);
    console.log('resolved input', someInput);
  })
...

/*
 * OUTPUT:
 *
 * washington DID meet conditions for the drinks-screwdrivers rule.
 * resolved input [ 1, 2, 3 ]
 * engine run completed
 */

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
CacheControlcommented, Nov 29, 2020

I’ve taken a crack at this in #237 - relatively easy fix since EventEmitter2 supports this out of the box. Comments welcome. I still have some docs to write and examples to tweak, but I think this is functionally code complete. Since this is a breaking change, I’ve got at least 1 other breaking change I’d like to into into this next release, so I’m shooting for a release in the next two weeks.

@iamzoh @naveen-shetty if you’d like to confirm the fix, you can npm install json-rules-engine@6.0.0-alpha-3.

2reactions
CacheControlcommented, Dec 22, 2020

This feature is now available in v6.0.0. See changelog

Read more comments on GitHub >

github_iconTop Results From Across the Web

Understanding the Event Loop, Callbacks, Promises, and ...
The callback just allows you to be informed of when an asynchronous task has completed and handles the success or failure of the...
Read more >
Asynchronous Programming with Async and Await - Visual Basic
A synchronous method returns when its work is complete (step 5), but an async method returns a task value when its work is...
Read more >
Is there a built-in way to have an async method fire an event ...
If you need callback, the simplest is to pass a delegate. Methods can implicitly work as delegates. Events are useful if you need...
Read more >
How to wait for an async task to complete inside an E2E test
Any Lambda function that is triggered asynchronously (e.g. from an EventBridge event, S3 upload, DynamoDB stream, SQS message); A Step Functions ...
Read more >
How to use promises - Learn web development | MDN
With a promise-based API, the asynchronous function starts the operation and returns a Promise object. You can then attach handlers to this ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found