I try to write the function that returns all results of asynchronous functions and execute a callback that push into an array and log the result of every async function.
As a waiter that brings all dishes when they are all done. I don't understand how to get the child arguments that should be returned as a result. The code of task and my not working solution is below:
The 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 not working 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)
}
});
}
Problem is 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 did not returned anything yet, so all you know is, that the functions have been called, but result.arr
is not yet populated.
You must move the doneAll(result.arr)
expression into child
callback, then it will be called by async function as it returns result.
Simpliest solution 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 enhance result object
var result = {
arr: []
, allCount: funcArray.length
, doneAll: doneAll
};
This shall work, but has one drawback -- position of results does not keep position of functions in funcArray
, the position of results is sorted by duration of async function, simply the first resolved would take first result etc. If this is a problem, you must pass also index to your child
function to store result at precious position in result array and then the check by arr.length
would not work, because JS array returns length as the highest index + 1, so if your last funcArray
would fulfill first, it'll fill last index and the length of result.arr
will be equal to this.allCount
, so for keeping order of result the same as funcArray
, you will need to store number of returned results as another number, increase that number with every new result and compare that number to allCount
.
Or decrease allCount like so
function child(idx, arg) {
this.arr[idx] = arg;
if (--this.allCount === 0) this.doneAll(this.arr);
}
And modify 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));
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With