I've come across this piece of code:
const results = await Promise.all(
[ Model1.find({}), Model2.find({}) ],
Model3.find({})
),
v1 = results[0],
v2 = results[1],
v3 = results[2]
which is invoking all()
with an array and a single object — `Model* are Mongoose models.
This is an easily fixed bug, but I'd like to understand how it is giving the resulting values, which are:
Model1
Model2
undefined
As explained in this answer on the comma operator, I expected only the Model3.find({})
promise to actually return data in results
, as the comma operator should evaluate the first operand but return its second operand (to Promise.all()
). But it's instead the other way around: results[0]
and results[1]
both contain data, while results[2]
(and thus v3
) is undefined
.
What am I missing?
The Promise.all() method takes an iterable of promises as input and returns a single Promise . This returned promise fulfills when all of the input's promises fulfill (including when an empty iterable is passed), with an array of the fulfillment values.
The comma operator ( , ) evaluates each of its operands (from left to right) and returns the value of the last operand. This lets you create a compound expression in which multiple expressions are evaluated, with the compound expression's final value being the value of the rightmost of its member expressions.
On the left-hand side of an assignment, the comma indicates that sequence unpacking should be performed according to the rules you quoted: a will be assigned the first element of the tuple, b the second.
Given an array of Promises, we have to run that in a series. To do this task, we can use then(), to run the next promise, after completion of a promise. Approach: The then() method returns a Promise, which helps us to chain promises/methods.
Your function call
Promise.all([ Model1.find({}), Model2.find({}) ], Model3.find({}))
// ^- First parameter -^ ^- second -^
You have passed only two promises to the Promise.all function -
[Model1.find({}), Model2.find({})]
.Model3
as the second parameter - Model3.find({})
.Model3
is passed but it is ignored by function - function takes only one parameter - the first one which must be an iterable
Promise.all(iterable);
Promise.all
just runs those promises which are defined in the first parameter and you get only results for Model1.find({})
and Model2.find({})
.
You think that results[2]
is the result of Model3.find({})
, but not. In this case it is an array which contains 2 items - results of two promises which are passed via array/iterable. When you want to access an index of the array which is greater then it's length - 1
, you will get undefined
.
In JavaScript you can pass to function how many arguments you want, but parameters will be assigned from left to right and those arguments which does not fit in the range of the parameters will be just ignored.
Look at the below example. I have passed 5 arguments to the function, but my function takes only first two, so the 3, 4, 5
are just ignored. You have this case.
function foo(a, b) {
console.log(a, b);
}
foo(1, 2, 3, 4, 5);
That's a parameter list rather than a single expression, so the comma operator does not apply - the array
[Model1.find({}), Model2.find({})]
is evaluated as the first argument passed to Promise.all
, and the second argument is
Model3.find({})
But Promise.all
only accepts one parameter, an iterable; the second parameter of Model3
is just ignored, and the result is just [Model1.find({}), Model2.find({})]
mapped to their resolve values. Since the passed array only has two values, the Promise.all
resolves to an array which also has only two values (index [2]
is undefined
).
If you had enclosed the Promise.all
call in another set of parentheses:
const results = await Promise.all((
[ Model1.find({}), Model2.find({}) ],
Model3.find({})
))
then you would be invoking the comma operator, because everything inside the second set of parentheses would get evaluated as a single expression while the interpreter attempts to come up with a value for the first (and only) parameter to be passed to the Promise.all
. (But Promise.all
accepts an iterable, not a Promise
, so the evaluation of the comma operator:
await Promise.all((
Model3.find({})
))
would then result in an error:
TypeError: undefined is not a function
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