JS: get internal function arguments in asynchronous functions and make a callback

I am trying to write a function that returns all the results of asynchronous functions and performs a callback that inserts into an array and registers the result of each asynchronous function.

Like a waiter who brings all the dishes when everything is done. I do not understand how to get the child arguments that should be returned as a result. The task code and my non-working solution below:

Task:

var dishOne = function(child) { setTimeout(function() { child('soup'); }, 1000); }; var dishTwo = function(child) { setTimeout(function() { child('dessert'); }, 1500); }; waiter([dishOne, dishTwo], function(results) { console.log(results); // console output = ['soup', 'dessert'] }); 

My inoperative solution:

 function child(arg) { this.arr.push(arg) } function waiter(funcArray, doneAll) { var result = { arr: [] }; let i = 0; const x = child.bind(result) funcArray.forEach(function(f) { f(x) i++; if(i == 2) { doneAll(result.arr) } }); } 
+8
javascript
source share
2 answers

The problem in this part:

 funcArray.forEach(function(f) { f(x) i++; if(i == 2) { doneAll(result.arr) } }); 

which is a synchronous function, so when you check if(i == 2) , you basically check that you have called all async functions, but they haven't returned anything yet, so all you know is that the functions were called, but result.arr is not populated yet.

You must move the doneAll(result.arr) expression doneAll(result.arr) to the child callback, then it will be called by the async function because it returns the result.

A simple solution that I can think of is writing your child as

 function child(arg) { if (this.arr.push(arg) === this.allCount) this.doneAll(this.arr); } 

and in your waiter function waiter improve the result object

 var result = { arr: [] , allCount: funcArray.length , doneAll: doneAll }; 

This will work, but it has one drawback - the position of the results does not save the position of the functions in the funcArray , the position of the results is sorted by the duration of the asynchronous function, just the first one will accept the first result, etc. If this is a problem, you should also pass the index of your child function to store the result at a valuable position in the result array, and then checking arr.length will not work, because the JS array returns the length as the highest index + 1, so if your last funcArray will execute first, it will fill in the last index, and the length of result.arr will be this.allCount , so to keep the order of the result equal to funcArray , you will need to save the number of returned results as a different number, increase this number with each new result and compare this number with allCount .

Or reduce allCount this way

 function child(idx, arg) { this.arr[idx] = arg; if (--this.allCount === 0) this.doneAll(this.arr); } 

And change your waiter function

 function waiter(funcArray, doneAll) { const result = { arr: [] , allCount: funcArray.length , doneAll: doneAll }; funcArray.forEach(function(f, i) { f(child.bind(result, i)); }); } 
+5
source share

Why not Promise ?

 function dishOne() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve('soup') }, 1000) }) } function dishTwo() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve('dessert') }, 1500) }) } 

Your waiter function:

 function waiter(dishes, callback) { return Promise.all(dishes).then(callback) } 

And you can use it like this:

 waiter([dishOne(), dishTwo()], function(results) { // Invoked when all dishes are done console.log(results) // ['soup', dessert'] }) 

Much easier to understand. Correctly?

0
source share

All Articles