When is callback of Promise.all() triggered - javascript

I just learned the concept of Promise.all() and am curious about its mechanism. From my limited knowledge of Promise, it passes a callback function in .then() method and invoke it in the definition. For instance:
var p = new Promise((resolve) => {
setTimeout(resolve, 2000);
setTimeout(() => {
console.log('4 sec function');
}, 4000);
});
p.then(() => {
console.log('2 sec function');
});
// result:
// 2 sec function
// 4 sec function
However, when we use Promise.all(), it triggers the callback when all promises fulfilled. As my guess, it triggers the callback in the promise which is fulfilled at last. In the instance below, my perspective of the result would be 'four'. However, it prints an array of the four numbers. Why does this happen? More specifically:
how many times is the callback actually triggered?
at which position in promises definition is the callback triggered?
what value is passed to the callback?
Here is the code I do research on:
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
setTimeout(resolve, 4000, 'four');
});
Promise.all([p1, p2, p3, p4]).then(values => {
console.log(values);
});
// print:
// one
// two
// three
// four

The callback is triggered once.
Either when all the promises are resolved or when one of them fails.
It receives an array of the values that all the promises resolved to.

Promise.race
There's also Promise.race which will resolve or reject as soon as the first of the promises (p1, p2, p3, or p4) resolves or rejects
// note: i've adjusted the delays from your original code
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 4000, 'one');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, 'two');
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'three');
});
var p4 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'four');
});
Promise.race([p1, p2, p3, p4]).then(fastest => {
console.log(fastest); // four
});

Related

Cannot control the order of asynchronous tasks using promise

I am facing an issue while using promise to control async tasks in JavaScript. In the following code, I wanted to output 'first' before 'second'. I used promise then block, but it didn't work as I wanted.
I used setTimeout to create different time delay, so that I can control async tasks order no matter what. Can anyone please help me understand why the code is not giving the output I wanted?
Here is the code:
let first = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log('first'));
}, 30);
});
let second = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log('second'));
}, 3);
});
first.then(second);
I don't believe that your code sample reflects your real situation, so I think it's not useful to solve the issue in your code sample.
Here is what I think you have:
// a task that returns a promise and takes some amount of time
const task = (name) => new Promise(resolve => setTimeout(() => resolve(name), Math.random() * 1000));
// a function using the task result
const taskDone = (name) => console.log(name + ' done');
// two instances of that task, finishing in unpredictable order
var action1 = task('task 1').then(taskDone);
var action2 = task('task 2').then(taskDone);
Run the snippet a few times to see that the result order changes. All you want a way to ensure that 'task 1 done' is always printed before 'task 2 done', even if task 2 finishes earlier.
The way to do this is to use Promise.all().
const task = (name) => new Promise(resolve => setTimeout(() => resolve(name), Math.random() * 1000));
const taskDone = (name) => console.log(name + ' done');
// set up tasks
var allTasks = [task('task 1'), task('task 2')];
// wait for results and evaluate
Promise.all(allTasks).then(allResults => allResults.forEach(taskDone));
allResults will be in the same order as allTasks, so 'task 1 done' will now always be printed before 'task 2 done'.
If you only have two tasks, you can be more explicit:
Promise.all([
task('task 1'),
task('task 2')
]).then(allResults => {
taskDone(allResults[0]);
taskDone(allResults[1]);
});
Wrap it to a function and create a closure and it will be called when you call it right the way.
let first = () => new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log('first'));
}, 30);
});
let second = () => new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log('second'));
}, 3);
});
first().then(second);
The first problem is that your promise constructor executor functions call console.log() before the promises are even fulfilled. Instead, you should wait to console.log() the values that promises are fulfilled with:
let first = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('first');
}, 30);
});
let second = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('second');
}, 3);
});
first.then((value1) => {
console.log(value1);
});
second.then((value2) => {
console.log(value2);
});
Now for the matter of sequencing the output, you can achieve this by awaiting the fulfilled value of second only after awaiting the fulfilled value of first, rather than awaiting both of them at the same time:
let first = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('first');
}, 30);
});
let second = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('second');
}, 3);
});
first.then((value1) => {
console.log(value1);
return second;
}).then((value2) => {
console.log(value2);
});
If you start two promises parallelly which are resolved by setTimeout then there is no way you can control your logs order.
You need to establish some order dependency between the two to resolve them in order.
One way could be as follows:-
let first = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log('first'));
}, 30);
});
first.then(() => {
let second = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log('second'));
}, 3);
});
return second;
})

How to implement Javascript promises for 3 functions?

I have 3 functions that both take their specific time to execute, then when they are all finished I need to trigger a certain function. With the following code, I am able to get the expected results with a single function but with multiple function I am failing to do it, please help me edit this code to wait for my 3 functions then console the data they returned.
var promise = new Promise(function(resolve, reject)
{
setTimeout(function()
{
resolve('hello world');
}, 2000);
});
promise.then(function(data)
{
console.log(data);
});
Javascript has an inbuilt method that waits for all you promises to resolve.
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
So for your case:
const promiseFunction = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
};
const promise1 = promiseFunction();
const promise2 = promiseFunction();
const promise3 = promiseFunction();
Promise.all([promise1, promise2, promise3])
.then((result) => {
console.log(result[0]);
console.log(result[1]);
console.log(result[2]);
})
Result is an array of values returned from each promise.
Promise.all() will take an array of promises as a parameter and wait for all promises to complete.
var promise1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('function 1 resolved');
}, Math.random() * 10000);
});
var promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('function 2 resolved');
}, Math.random() * 10000);
});
var promise3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('function 3 resolved');
}, Math.random() * 10000);
});
Promise.all([promise1, promise2, promise3])
.then(resArr => {
console.log(resArr)
})
You can catch the error occurred in any of promise by using .catch() attached to promise.all()
Promise.all([promise1, promise2, promise3])
.then(resArr => {
console.log(resArr)
})
.catch(error => console.log(error))
You can refer this code, where the function is called 3 times, and there are 2 ways you can do this and you can also see a different behavior.
// Function which returns a promise and resolves in 2 seconds
const promiseFunction = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
};
// Asynchronous function 1
const asyncFunction1 = async() => {
console.log(await promiseFunction());
console.log(await promiseFunction());
console.log(await promiseFunction());
}
// Asynchronous function 2
const asyncFunction2 = async() => {
let a = promiseFunction();
let b = promiseFunction();
let c = promiseFunction();
console.log(await a);
console.log(await b);
console.log(await c);
}
See the difference between
asyncFunction1() and asyncFunction2() calls
using async/await is a ES6 way, you can also do .then() chaining
promiseFunction().then(() => {
//function body here
})
you can chain your promises, like so:
new Promise(function(resolve, reject) {
resolve(1);
}).then(function(result) {
// result is 1
return result * 2;
}).then(function(result) {
// result is 2
return result * 2;
}).then(function(result) {
// result is 4
return result * 2;
});
To keep the results of all functions, you could have the result be an array, and push the new result to it every time. Like so:
new Promise(function(resolve, reject) {
resolve([0]);
}).then(function(result) {
result.push(1);
return result;
}).then(function(result) {
result.push(2);
return result;
}).then(function(result) {
result.push(3);
console.log(result);
return result;
});
This will log [0,1,2,3]
If the promises are independent of each other then use Promise.all:
Promise.all([promise1, promise2, promise3]).then(console.log);

Promise.all is not waiting for all promises to get resolved

Promise.all is not waiting for all promises to get resolved.In Below code I am trying to replicate a scenarios where depending on some service (two services) response I would be setting the array and then when all service call is done, process.all will give the captrued value. However this does not work.
let errorPromises = [];
setTimeout(() => {
errorPromises.push(new Promise((resolve, reject) => resolve(1000)));
}, 2000);
setTimeout(() => {
errorPromises.push(new Promise((resolve, reject) => resolve(3000)));
}, 1000);
let promise = Promise.all(errorPromises);
promise.then(data => {
console.log("All done", data);
});
it should print "All done [1000,3000]" but it prints "All done []"
Please help.
This happens because you are creating promises after the timeout. You need to wrap the timeout in Promise instead
let errorPromises = [];
errorPromises.push(new Promise((resolve, reject) => {
setTimeout(() => resolve(1000), 2000);
}));
// or a one-liner
// errorPromises.push(new Promise(resolve => setTimeout(() => resolve(1000), 2000)));
/// repeat with all others...
let promise = Promise.all(errorPromises);
promise.then(data => {
console.log("All done", data);
});
This happens because you create the promise, before your error promises are in the array. It will take 1 and 2 seconds for the promises to be pushed in the array with timeout, so you get what you want if you wait that the array contains something, for example if you set timeout for the promise too, which should happen AFTER the promises are pushed in to the array.
let errorPromises = [];
setTimeout(() => {
errorPromises.push(new Promise((resolve, reject) => resolve(1000)));
}, 2000);
setTimeout(() => {
errorPromises.push(new Promise((resolve, reject) => resolve(3000)));
}, 1000);
setTimeout(() => {
let promise = Promise.all(errorPromises);
promise.then(data => {
console.log("All done", data);
});
}, 3000);
So basically you trigger the console log instantly, and then your array is still empty because of the timeout.
let errorPromises = [];
errorPromises.push(new Promise((resolve, reject) => resolve(1000)));
errorPromises.push(new Promise((resolve, reject) => resolve(3000)));
let promise = Promise.all(errorPromises);
promise.then(data => {
console.log("All done", data);
});

i need to returns a `Promise` that is fulfilled when all promises in `input` * are settled (meaning, either resolved or rejected) [duplicate]

This question already has answers here:
Wait until all promises complete even if some rejected
(20 answers)
Closed 5 years ago.
promiseSettle must returns a Promise that is fulfilled when all promises in input are settled (meaning, either resolved or rejected).
The fulfillment value will be an array of objects, each having the following signature:
#typedef {Object} Settlement
#property {boolean} isFulfilled - whether the promise resolved
#property {boolean} isRejected - whether the promise rejected
#property {*=} value - the value (if any) with which the promise was resolved
#property {*=} reason - the reason (if any) with which the promise was rejected
#param {Array.>} input - an array of Promises
#return {Promise.>}
function promiseSettle(input) {
let promiseArray = [];
for (let i = 0; i < input.length; i++) {
Promise.resolve(input[i]).then(output => {
promiseArray.push(output);
console.log(promiseArray);
}, reason => {
promiseArray.push(reason);
})
}
}
// testing data
var p1 = new Promise((resolve, reject) => {
setTimeout(reject, 1, "first promise of 1 sec");
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 1, "second promise of 2 sec");
})
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 1, "rejected promise");
})
promiseSettle([p1, p2, p3])
Can someone help with this? I am not sure how to return the promise with the expected parameters.
This should do it.
function promiseSettle(inputList) {
let promiseArray = inputList.map(
input => {
return new Promise((resolve, reject) => {
input.then(
value => {
resolve({
isFulfilled: true,
isRejected: false,
value
});
},
reason => {
resolve({
isFulfilled: false,
isRejected: true,
reason
});
}
);
});
}
);
return Promise.all(promiseArray);
}
// testing data
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1, "first promise of 1 sec");
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2, "second promise of 2 sec");
})
var p3 = new Promise((resolve, reject) => {
setTimeout(reject, 1, "rejected promise");
})
promiseSettle([p1, p2, p3]).then(
promises => {
console.log(promises);
}
);

break promise.all on catch

I have a promise implementation like the one on MDN:
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "one");
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, "two");
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, "three");
});
var p4 = new Promise((resolve, reject) => {
setTimeout(resolve, 4000, "four");
});
var p5 = new Promise((resolve, reject) => {
reject("reject");
});
Promise.all([p1, p2, p3, p4, p5]).then(value => {
console.log(value);
}, function(reason) {
console.log(reason)
});
This does fast error-catching:
Promise.all is rejected if one of the elements is rejected and Promise.all fails fast: If you have four promises which resolve after a timeout, and one calls reject immediately, then Promise.all rejects immediately.
However, the functions inside a promise don't stop running. What I want, is that if one function does a reject, the other functions stop running (preventing useless messages to a user).
Example of a promise that keeps running after the reject:
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "one");
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, "two");
});
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, "three");
});
var p4 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Don\'t show this after a reject!');
resolve();
}, 4000);
});
var p5 = new Promise((resolve, reject) => {
reject("reject");
});
Promise.all([p1, p2, p3, p4, p5]).then(value => {
console.log(value);
}, function(reason) {
console.log(reason)
});
There's nothing general in promises that will do that, since stopping the other actions is action-specific, not generic. For instance, in your example, you have to keep the timer handle and then use clearTimeout; with an ajax request, you might need to do an abort call; with something else, it would be something else.
So you'll need to handle this in your real code in a way that's specific to your real code, using .catch on the promise returned by Promise.all (or the second arg to then as you have).
In the specific code in your question, it would look something like this (I've added output to the timers we don't cancel as well), but again it will vary depending on what you're cancelling:
var timersToReject = [];
var p1 = new Promise((resolve, reject) => {
setTimeout(v => {
console.log("resolving " + v);
resolve(v);
}, 1000, "one");
});
var p2 = new Promise((resolve, reject) => {
setTimeout(v => {
console.log("resolving " + v);
resolve(v);
}, 2000, "two");
});
var p3 = new Promise((resolve, reject) => {
setTimeout(v => {
console.log("resolving " + v);
resolve(v);
}, 3000, "three");
});
var p4 = new Promise((resolve, reject) => {
timersToReject.push(setTimeout(() => {
console.log('Don\'t show this after a reject!');
resolve();
}, 4000));
});
var p5 = new Promise((resolve, reject) => {
reject("reject");
});
Promise.all([p1, p2, p3, p4, p5]).then(value => {
console.log(value);
}, function(reason) {
console.log(reason)
timersToReject.forEach(t => clearTimeout(t));
});

Categories

Resources