4

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.

3
  • You don't need to define functions as async to await their results. Any value can be awaited, not just promises. Commented Dec 18, 2020 at 11:24
  • Do you want the async parts to happen concurrently? Commented Dec 18, 2020 at 11:25
  • @Bergi No, serial execution is fine for now. But it would be wonderful to know if I can achieve concurrency also. Commented Dec 18, 2020 at 11:26

6 Answers 6

7

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);
});
Sign up to request clarification or add additional context in comments.

4 Comments

Not sure why you mention objects containing promises, the OP doesn't have these anywhere?
@Bergi in the OP question, it kinda looks like (s)he wants to retrieve the results in an object with the provided keys ('func1', 'func2', ...). I'm not sure why (s)he nesting those objects in the array values, I can only guess!
Oh, wow, I skipped over that completely. The name listOfFuncs implied an array of functions to me…
Doing listOfFuncs.flatMap(o => Object.values(o).filter(v => typeof v === "function")) will return a clean, flat Array
4

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()));
}

Comments

1

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...

Comments

0

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)) 

2 Comments

Why make the distinction at all? You don't need to pass undefined to Promise.all.
you can also use filter
-1

Maybe this is a solution for you:

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

5 Comments

its an array of async function, which are not wrapped into an object. You can also just put the async function you like into the array without using (async() => {})()
Hey Paul, thanks for answering but that requires all the functions to be promise.
simplest solution: wrap all function into a async function
Hey Paul, but that would create a lot of un-needed promises. I heard wrapping normal function with promise has perf impact.
@Sathish wrapping normal function with promise has perf impact Generally speaking, these days that's really not much of an issue now that Browsers have native support for Promises & async / await. etc. To give you an example, recently I created a async readByte function in Node.js, that would chunk the data in, (reason for the promise). And it was no slower than reading the whole binary file in, and traversing the buffer. I was pretty surprised too, as I was expected some slow down.
-1

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);
    }
  }
}

2 Comments

You can remove that if/else and just await all of them. It does not need to be async to be awaited
Also, that if would not work if the function returns a Promise without using async/await. The constructor would just be Function, so undistinguishable from a sync function before calling it

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.