Need help understanding Promise.all and async - javascript

This is the code I'm going to be talking about.
view: async (req, res, next) => {
let formato = req.query.formato || 'details';
try {
let mode = Client.getMode(formato);
let options = {
columns: mode.columns,
withRelated: mode.withRelated,
};
let client_promises = [ ]
req.query['ids'].map( id => {
let client = Client.findById(id, options);
client_promises.push(client)
})
let response = await Promise.all(cliente_promesas)
return res.json({ success: true, data: response });
} catch (error) {
return next(error);
}
},
I understand that the .map function iterates over an array of ids, which then is passed to Client.findById so it can return a promise to be fulfilled, getting the client's data when it resolves.
Now, those promises are pushed to an array, which then is passed to Promise.all, but I dont really understand where they are being resolved. Isnt Promise.all just passing a resolved promise when all promises in the array are resolved?
I also understand that await just makes the code wait for the resolution of the promise before continuing.
But where are the promises in client_promises being resolved?
I know this is basic but for the life of me I cant seem to read enough manuals and guides to be able to understand this.

All Promise.all does is wait for every Promise in the array passed to it to be done. It doesn't "fire" them. In fact every Promise in the array passed might even be in a resolved state. The Promises are "hot" from the moment they are created.
For your example, the function to get an item by id starts immediately and synchronously returns a Promise that will eventually resolve to whatever object is retrieved by id. This is key: the returning of the Promise is not the asynchronous part, it's the resolution.
Promise.all makes all the promises one big promise.
Consider the example below. I take 3 ids and do exactly what you do, I call findById and put what is returned by the function (a Promise) into an array, promises. In the findById, I also add an extra .then call to demonstrate how these Promises are "hot" and not waiting to be called by Promise.all. Also note that I still return the p.
By the time we actually get to the Promise.all, all of the Promises in promises have actually resolved, that's why you see them print to console first. They all take 100-600 milliseconds to resolve, but we actively wait a full 1000 milliseconds before we call Promise.all.
Unfortunately (kind of) there is no API to reveal the state of a Promise using the native implementation. I think there used to be ways with user land libraries like Bluebird and jQuery, but not with how the browser does it. Otherwise, we could inspect the Promise right before calling the Promise.all and see whether they were resolved or pending.
/**
* Utility function to wait a duration before resolving
* #param {number} [delay=100] How long to wait
* #returns {Promise<undfined>} Promise that will resolve after delay
*/
function timeout(delay = 100) {
return new Promise(resolve => {
setTimeout(resolve, delay);
});
}
/**
* Simulated find function that returns an object
* #param {number} id The id of the object to get
* #returns {Promise<object>} The fake DB object
*/
async function findById(id) {
await timeout((Math.random() * 500) + 100);
return {
id,
data: `foo-${id}`
};
}
(async function() {
const idsToGet = [1, 2, 3];
const promises = idsToGet.map(id => {
// Call to get the object
const p = findById(id);
// Let's add another listener here to know when they are done
p.then(r => {
console.log(`${r.id} is done`);
});
// Still return the actual Promise created by findById
return p;
});
// Just for kicks
await timeout(1000);
// Wait for EVERYTHING to be done
const res = await Promise.all(promises);
console.log(JSON.stringify(res, null, 4));
}());

Promise.all() takes list/ array of unresolved promises.
It is kind of like a big one promise that takes all the unresolved promises and until all of them are resolved the Promise.all() is unresolved.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
In the above example, you can see that we are passing 3 promises to Promise.all()
and when all of them are fulfilled then only .then will be triggered.
in your case:
Client.findById(id, options);
must be returning a promise object which you are pushing into client_promises array.
and then feeding them to Promise.all(), until this point, your promises are just promises they are not resolved.
promise.all also returns a promise and as you have put await in front of it.
it will wait until of them re resolved and give you an array of resolved promises.

The promises are actually "fired" (that is, the code starts to run) when you do let client = Client.findById(id, options);. await Promise.all then waits until all of them are resolved or one of them is rejected in which case Promise.all also rejects with the value of the first promise rejection.

Related

Is promise.all used to de-capsulate promise value?

I'm trying to understand what Promise.all() is doing exactly.
As far as I understand,
promise is something which holds a return value for asynchronous execution in javascript.
So the values encapsulated(?) by promise is not directly accessible in sychronoous running.
(Terminology warning! I'm going to use analogy to show how I see promise and promise.all())
To get a direct access to the promisified values,
I need to peel off a shell called promise, which is
promise.all at the end of asynchronous function.
after this 'peeling-off', I could access to a synchronously available values.
Am I understanding Promise.all correctly?
Thank you so much.
Example is from mdn website.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
all the promises and just synchronous value (const promise2) were successfully approached by synchronous execution 'console.log()'.
If I don't peel off the promise shell like below,
Promise.all([promise2, promise3]).then((values) => {
console.log(values);
console.log(promise1)
});
The values that js spits out are as below.
Array [42, "foo"]
[object Promise]
Please feel free to criticize my analogy and
let me know if I understand something wrong.
Thank you in advance.
Your analogy is a very interesting one, but I'm sure it's a common perception of how Promises work. Promises are constructs that manage deffered values - values that will be at some stage, time-dependent. Using the term "wrapping" makes it sound like the value is already there, when in fact it most likely isn't.. yet.
A promise can be used to represent the response from an API, or perhaps a lookup from a database - both of these take time - something synchronous code can't handle. You might make a request, and get a promise in response. You can then use this promise as a helper to get the value you intended to receive.
When you call promise.then(value => ...), the then function is waiting until the value is ready. Inside the promise, something must call resolve(value) before the then function will fire (note that the promise can also be rejected via reject, in which case promise.catch would be fired). Here's a full example:
const promise = new Promise(resolve => {
setTimeout(() => {
resolve(10);
}, 2000);
});
promise.then(value => {
// This fires after about 2 seconds
console.log(value); // 10
});
So the trick here is to visualise that getting results from a promise occurs on a different "thread".. perhaps in a callback or after an await call. Also, once a promise has been resolved, calling then a second time will return the same value as previously, and it will be faster, but still not instant as promises "values" are always asynchronously returned.
Promise.all waits until all the provided promises have resolved. What is returned by Promise.all is an array containing all of the resolved results in order. So if you have the following:
const promise1 = Promise.resolve(12); // Asynchronously returns 12
const promise2 = new Promise(resolve => {
setTimeout(() => {
resolve(10);
}, 2000);
});
Promise.all([promise1, promise2]).then(results => {
// results will be [12, 10], and will fire after about 2 seconds
});
// here, even though we're after the promise assignments,
// the values to BOTH of the promises are still not yet defined (not resolved)
TLDR; A promise is a class that records a value once its provided function resolves. The then method will call its callback with the value once it is set by the promise function (by calling resolve or reject).

Resolved promise waits for inner promise to be resolved

I'd like to have a promise to a promise, something like this:
let first = new Promise(resolveFirst => {
setTimeout(() => {
resolveFirst("resolved!");
}, 2000)
});
let second = new Promise(resolveSecond => {
setTimeout(() => {
resolveSecond(first);
}, 10)
});
second.then(secondValue => {
console.log("second value: ", secondValue);
secondValue.then(firstValue => {
console.log("first value: ", firstValue);
});
});
So that the console.log("second value: ", secondValue); is printed after 10 millies and then the console.log("first value: ", firstValue) will be printed 2000 afterwards.
What happens though is that I get:
second value: resolved!
And an error:
Uncaught (in promise) TypeError: secondValue.then is not a function
Together after 2010 millies.
It seems that when the second promise is resolved, and the first one is returned then it automatically waits for this first promise to resolve as well.
Why is that? And how do I break between them?
Edit
Here's a solution that was posted on facebook using Array.reduce():
const runPromisesInSeries =
ps => ps.reduce((p, next) =>
p.then(next), Promise.resolve());
const delay = d => new Promise(r => setTimeout(r, d));
runPromisesInSeries([() => delay(1000), () => delay(2000)]);
//executes each promise sequentially, taking a total of 3 seconds to complete
(https://www.facebook.com/addyosmaniofficial/photos/a.10151435123819601.1073741825.129712729600/10155386805609601/?type=3&theater)
By design, a promise that is resolved with a second promise takes on the value and state of the 2nd promise after the 2nd promise becomes settled (fulfilled or rejected), waiting for it to be settled as necessary.
It is not possible to fulfill a promise with a promise or thenable object (any object with a then method`) value unless the promise library is seriously flawed (older versions of JQuery would fulfill their own promise objects with a promise object from a different library).
Promise rejection does not have the same checks in place and will happily pass a promise object down the chain without waiting for it to be resolved. So you could reject the 2nd promise with the first and pick it up in a catch clause.
Although technically feasible I strongly advise against doing so except to prove it can be done - it's a maintenance issue for a third party trying to understand the code.
While you could pass the promise as an object property down the success channel of a promise chain, re-analysis of promise composition may provide a better or more standard solution. E.G. Promise.all waits for for two or more independent promises to be fulfilled before proceeding on a common task.
That's just part of their charm: If your promise A resolves to a thenable B, then A resolves only after B resolves, and A takes B's resolved value. That's part of both Promises/A+ and ES6, as part of the "promise resolution procedure".
You can see some of the advantages of that if (say) one action requires an action before its completion (like login), or requires loading another page of results, or has retry logic of its own.
Though it's not very idiomatic, if you want to return an unresolved promise immediately without waiting for it, you can do so by passing it in an {object} or [array], but there might not be much reason for that: Other than waiting for its completion, what would you do with a returned promise?
You can chain then only to a promise.
secondValue is not a promise, it's just the value that returned when you called the second promise. or the resolved value from that promise.
If you want that to work, try this:
let first = new Promise(resolveFirst => {
setTimeout(() => {
resolveFirst("resolved!");
}, 2000)
});
let second = new Promise(resolveSecond => {
setTimeout(() => {
resolveSecond(first);
}, 10)
});
second.then(secondValue => {
console.log("second value: ", secondValue);
/** first is a Promise. return a promise, and the next "then"
* will pass the result of that Promise.
*/
return first
})
.then(firstValue => {
console.log(firstValue)
})
EDIT 1
Further explanation for why second promise resolved the eventual value (string) of the first promise, and not the promise itself, can be found on mdn:
Promise.resolve()
if the value was a promise, that object becomes the result of the call to Promise.resolve; otherwise the returned promise will be fulfilled with the value.

Given a set of Promises, how do I forcefully resolve with the response of the last Promise, while ensuring all Promises resolve successfully?

Promise One (resolves in 200ms)
Promise Two (resolves in 400ms)
Promise Three (resolves in 1000ms)
Promise Four (resolves in 300ms)
Promise Five (resolves in 500ms)
These Promises will resolve in the following order
0 - Requests Start
100
200 - Promise One
300 - Promise Four
400 - Promise Two
500 - Promise Five (Most recent application state)
600
700
800
900
1000 - Promise Three (Stagnant application state)
Given that in my app, the response of the Promises dictates the application state, when an older Promise resolves more slowly than a newer one, my application state will be stagnant.
A potential implementation would be to simply not start the next request until a previous request has finished, but this would hang user progress considerably.
EDIT:
I might have left out a bit of necessary context. There's no way for me to tell when a Promise will be added. Promise.all can't have items added to it after it has started, so Promise.all might not work for me.
For a more normal use-case the provided answers probably would have worked nicely, but unfortunately my API is a bit too chatty.
So you want all promises to actually complete but go forward only with the newest promise's value?
Promise.all([p1, p2, p3, p4, p5]) // resolves when all promises have resolved
.then((results) => {
// results is an array of resolved values
})
Would Promise.all() work for you? Bear in mind that it rejects if any of the promises reject.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Using ES2017 async / await, you can improve upon the readability of #MauricioPoppe's answer by doing the following:
const pTimeout = (value, timeout) =>
new Promise(resolve => setTimeout(resolve, timeout, value))
// takes the same arguments as Promise.all but resolves with the
// resolution value of the last element
async function resolveWithLast (promises) {
const results = await Promise.all(promises)
return results.pop()
}
resolveWithLast([
pTimeout(1, 200),
pTimeout(2, 400),
pTimeout(3, 1000),
pTimeout(4, 300),
pTimeout(5, 500)
]).then(value => console.log(value))
Create a new promise that resolves when all the other promises have been resolved (done with Promise.all), then chain another promise that resolves with the value of the last element of the iterable sent to Promise.all
const pTimeout = (value, timeout) =>
new Promise(resolve => setTimeout(resolve, timeout, value))
// takes the same arguments as Promise.all but resolves with the
// resolution value of the last element
function resolveWithLast (promises) {
return Promise.all(promises)
.then(results => results.pop())
}
resolveWithLast([
pTimeout(1, 200),
pTimeout(2, 400),
pTimeout(3, 1000),
pTimeout(4, 300),
pTimeout(5, 500)
])
.then(value => console.log(value))

JavaScript Serial Promises with setTimeout

I am working on building Promise chain with setTimeouts in them. All of the Promises needs to be run in series not parallel. I am using Bluebird module to achieve serial flow of Promise execution.
Can someone please explain me why this code gives me output of 1,2,3,4 instead of 4,3,2,1?
var bluebirdPromise = require('bluebird');
function p1(value) {
return new Promise(function(resolve, reject) {
setTimeout(function(resolve) {
console.log(value);
resolve;
}, value * 1000);
});
}
var arr = [p1(4), p1(3), p1(2), p1(1)];
bluebirdPromise.reduce(arr,
function(item, index, length) {
}).then(function (result) {
});
There are several issues:
The console.log you have is not made dependent on a previous resolved promise. There is just the time-out that determines when the output will happen. As you create all four promises at the "same" time, and thus all four setTimeout calls are invoked simultaneously, their callbacks will be called at the determined time-out. It does not matter how you chain promises afterwards... To solve this, you need to move the console.log in a then callback, because that callback will only be executed when the previous promise in the chain has been resolved.
The resolve function is not called in your code. You need to add parentheses.
The resolve parameter of the setTimeout callback hides the real function with the same name: you need to remove that parameter.
Here is the suggested correction. For this snippet I have replaced the bluebird reduce with a standard Array#reduce, but it would work similarly with bluebird's reduce:
function p1(value) {
return new Promise(function(resolve, reject) {
setTimeout(function() { // ***
resolve(value); // ***
}, value * 1000);
});
}
var arr = [p1(4), p1(3), p1(2), p1(1)];
arr.reduce(function(promise, next) {
return promise.then(_ => next).then( value => {
console.log(value); // ***
return value;
});
}, Promise.resolve());
If you have a promise-creator function, p, and you want to run a sequence of promises in serial, there's no need for you to load an array with promises – instead, just let it be a normal array of values
Notice I'm not using value * 1000 here either – in your code, you thought you had to artificially choreograph the promises to fire in a particular order using calculated setTimeout delays; that's just not the case. Carefully observe the evaluation of the code below to see how we have a 1-second delay between each promise and .then keeps things in order
Also note that this code will start outputting immediately after the first promise resolves – as opposed to waiting for all promises to resolve before outputting all values
const p = x =>
new Promise(f =>
setTimeout(f, 1e3, x))
const arr = [4,3,2,1]
arr.reduce((acc, x) =>
acc.then(() => p(x)).then(console.log), Promise.resolve())
OK, so you have these promises running in serial order, but why? Unless later steps somehow depend on the result of earlier steps, there's no reason why you'd want to slow these down – ie, each promise's result is not dependent on the others, so just calculate them all as fast as possible. But you're worried that the order will be lost, right? Don't worry, everything will be OK – I'll even use a random delay to show you that the time each promise takes matters not
const p = x =>
new Promise(f =>
setTimeout(f, 1e3 * Math.random(), x))
const arr = [4,3,2,1]
arr.map(p).reduce((acc, x) =>
acc.then(() => x).then(console.log), Promise.resolve())
So now, all of the promises can start immediately, and output will start as soon as the first promise is resolved (unlike Promise.all which would wait for all promises to finish before any values are available to you).
I only mention this as an alternative because the example you provided shows no real need for the promises to be executed in serial. You may have naively simplified your problem's domain, but only you know whether that's the case.

Dynamic array promise sequence

Im getting dynamic (the number of promises can be change each per runtime) array of promises, Now I want that after every resolve or reject
to handle the returned promise before promises array will be finished .
I try with promise all but it's continue after all the promises has done.
if the array (of promises) wasn't be dynamic so I can simply use something like this
but I dont know how much promises I have in the array and I want after every promise fulfilled or ... to proceed with the answer of it and not wait until all the promises will done
firstMethod()
.then(secondMethod)
.then(thirdMethod);
We are using Q ...
Is it possible ?
Update (example )
Lets say I've the promise array
[promise1,promise2,promise3, promiseN]
Now I want that when promise1 finish to process to handle it with then and not wait that all promises will be finished .
The biggest issue here is the array can have N promises and I dont know in RT how much promises I will get until I got this array therefore I cannot use simply then
Update 2 (to clarify a bit more :-) ) The tricky part...
If I Know the size of the array beforehand I can use simply then , 5 entry in the array 5 chain with then but here the tricky part is that I dont know beforehand how much promises I will have in the array of the promises ....
If the goal is to chain each promise with same then but to not wait for all promises to complete, this is just a loop:
for (const promise of promiseArray) {
promise.then(...)
}
If promise results should be joined together in the end, the array can be additionally processed by all:
Promise.all(promiseArray.map(promise => promise.then(...)))
.then(...)
Notice that behaviour for all applies here. If there is uncaught rejection in promise array, only the first rejection will be caught.
This is true for most promise implementations. There may be Q methods that allow do do this easier.
you can use Promise.race()
The Promise.race(iterable) method returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise.
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
If you don't need to chain promises. You could just do:
var tasks = [[promise1, handler1], [promise2, handler2], [promise3, handler3]];
tasks.forEach(function (task) {
var promise = task[0];
var handler = task[1];
promise.then(handler)
});

Categories

Resources