Skip to content

Instantly share code, notes, and snippets.

@telekosmos
Last active July 16, 2020 11:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save telekosmos/d0cb7000a6d16a539d14dfa0da3b1a96 to your computer and use it in GitHub Desktop.
Save telekosmos/d0cb7000a6d16a539d14dfa0da3b1a96 to your computer and use it in GitHub Desktop.
Javascript recipe to check asynchronous conditions concurrently

Check conditions concurrently

A system is said to be concurrent if it can support two or more actions in progress at the same time. A system is said to be parallel if it can support two or more actions executing simultaneously.

The Art of concurrency

We come up with a (curried) function with which we are able to evaluate a variable number of possibly asynchronous functions over a value in a concurrent way.

const checkConditions = (...predicates) => (value) =>
  Promise.all(predicates.map((p) => p(value))).then((results) => results.reduce((acc, value) => acc && value, true));

Description

The ...predicates means a variable number of arguments. Calling the function with just predicate functions will produce a function which accepts just a value to apply the functions over.

The body of the function is quite easy to understand:

  • Promise.all expects an array of promises, which is achievec by a mapping over the predicate functions
  • The mapping will produce an array of promises, already resolved if the function applied is synchronous, or waiting for being resolved or rejected if they are asynchronous.
  • In this mapping the asynchronous functions are being executed in a concurrent way, speeding up the process.
  • In the then block the results of the promises are reduced, resolving to a truthy value only if all the promises were resolved to truthy values.
  • Remark: when calling this function we'll need a catch block if promises are used or enclose the call in a try/catch if using async/await.

Examples

It is possible from the function above set custom conditions based on a list of functions, either synchronous or asynchronous.

// synchronous predicates
const divBy2 = (v) => v % 2 === 0;
const divBy3 = (v) => v % 3 === 0;
const divBy5 = (v) => v % 5 === 0;
const divBy2_3_5_SyncCheck = checkConditions(divBy2, divBy3, divBy5);
// when calling, given the function predicates are synchronous, they will run sequentally

// asynchronous predicates
const divBy2Async = (v) => new Promise((resolve) => setTimeout(() => resolve(v % 2 === 0), 1000));
const divBy3Async = (v) => new Promise((resolve) => setTimeout(() => resolve(v % 3 === 0), 500));
const divBy5Async = (v) => new Promise((resolve) => setTimeout(() => resolve(v % 5 === 0), 1000));
const divBy2_3_5AsyncCheck = checkConditions(divBy2Async, divBy3Async, divBy5Async);
// here, when called, the functions will run concurrentlly

const mixedCheck = checkConditions(divBy2, divBy5Async);

Calls are done in a similar way:

const isDivBy2_3_5Sync = await divBy2_3_5_SyncCheck(10); // false
const isDivBy2_3_5ASync = await divBy2_3_5AsyncCheck(30); // true
const isDivBy2_5 = await mixedCheck(10); // true

But don't forget catching errors

const isaNumberAsync = (v) =>
  new Promise((resolve, reject) =>
    setTimeout(() => {
      if (typeof v === 'number') resolve(true);
      else reject(new Error(`Not a number: ${v}`));
    }, 1800)
  );
const isDivBy2_0 = checkConditions(divBy2Async, isaNumberAsync);

// using await
try {
  const result = await isDivBy2_0(29);
} catch (err) {
  console.error('Process error');
}

// or using promises
isDivBy2_0(29)
  .then((result) => console.log('Process result'))
  .catch((err) => console.error('Process error'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment