How to execute a array of synchronous and asynchronous functions in Node.js

Viewed 1597

I have config like JSON where we can define any JavaScript functions inside. Now I have execution function which would take that array of functions and execute. How can I do that?

const listOfFuncs = [
  {
    "func1": (...args) => console.log(...args)
  },
  {
    "func2": async (...args) => {
      return await fetch('some_url');
    }
  }
]

function execute() {
  // how to execute the above array of functions now ?
}

// Should this be called as await execute()? 
execute();

As you can see one function sync & another function as async & await. Defining everything function as async & await seems bad ( creating a lot of new promises ) + I can't define all function as synchronous also.

Thanks for your answer in advance.

6 Answers

You can use Promise.all() to resolve an array of promises.

Values other than promises will be returned as-is

const listOfFuncs = [
  () => 45,
  async () => new Promise(resolve => {
      setTimeout(() => resolve(54), 100);
  }),
  () => Promise.resolve(34)
];

// Convert to list of promises and values
const listOfMixedPromisesAndValues = listOfFuncs.map(func => func());

// Promise.all returns a Promise which resolves to an array
// containing all results in the same order.
Promise.all(listOfMixedPromisesAndValues).then(result => {
  // Logs: [45, 54, 34] after 100ms
  console.log(result)
});

There is no native function to resolve an object containing promises.

However some libraries implement alternative Promise API, to make it easier to use more complex pattern (cancellation, races, ...). The most well known is Bluebird.

It implements a Promise.props methods which almost do what you want: http://bluebirdjs.com/docs/api/promise.props.html

var Promise = require("bluebird");

Promise.props({
    pictures: getPictures(),
    comments: getComments(),
    tweets: getTweets()
}).then(function(result) {
    console.log(result.tweets, result.pictures, result.comments);
});

You should simply treat all your functions as potentially returning a promise. No need to distinguish them, just await the result. The execution will carry on immediately if it was not a thenable (read: non-promise) value. So just write

async function execute() {
  for (const func of listOfFuncs) {
    await func();
  }
}

If you want the asynchronous tasks to run concurrently, just collect all values into an array and pass them to Promise.all. It deals with non-promise elements just fine as well.

async function execute() {
  await Promise.all(listOfFuncs.map(func => func()));
}

Solution without promises. Use process.nextTick(callback) or setImmediate

const listOfFuncs = [
    {
        "func3": setImmediate(execute, "setImmadiate executes after process.nextTick")
    }, 
    {
        "func2": process.nextTick(execute, "process.nextTick executes after sync function but before setImmediate")
    },
    {
        "func1": execute
    }
]

function execute() {
    console.log("executing...", arguments[0]);

}

execute(listOfFuncs);

// results: 
// executing...[...]
// executing...process.tick
// executing...setImmediate...

In this example, you create an array of the executed functions and use the Promise.all() with a map to get all promisses if the function results. (in a case of an async function the function returns a promise-value, which you can await)

function isPromise(promise) {  
    return !!promise && typeof promise.then === 'function'
}
let functions = [
  (() => {})(),
  (async () => {})()
];
await Promise.all(functions.map(function_result => (isPromise(function_result) ? function_result : undefined)) 

Maybe this is a solution for you:

await Promise.all([
  (async () => {
    //Your Function here
  })(),
]);

Ideally having everything async would be cleaner. But if not, this would work :

async function execute(...args) {
  for (let i = 0; i < listOfFuncs.length; i++) {
    if (listOfFuncs[i].constructor.name === "AsyncFunction") {
      await listOfFuncs[i](...args);
    } else {
      listOfFuncs[i](...args);
    }
  }
}
Related