0

I know of the Model.create(documentsArray) and also Model.bulkSave(documentsArray) mongoose API's.

I was just experimenting with the Promise.all the following way -


const promises = []
for(let i=1; i++; i<1000) {
 const promise = new UserModel({userId: Math.random() * 1000}).save()
 promises.push(promise)
}

console.time('t1')
await Promise.all(promises)
console.timeEnd('t1') // takes about 329.189ms

If I replace the insertion logic by -

console.time('t2')
for(const promise of promises) {
  await promise
}
console.timeEnd('t2') // takes about 335.268ms

As commented above both t1 and t2 are nearly same! Why Promise.all don't sends all save requests to the database at once if I understand it correctly. What am I missing here?

NOTE: above both blocks are meant to

shubham
  • 367
  • 6
  • 14
  • You don't have to await the promise again. if you use `result = await Promise.all(promises)` then `result` will be an array of all the results of each individual promise, in the same order the promises were in. – Wyck Apr 28 '22 at 13:32
  • 1
    `Promise.all()` doesn't *send* anything. It only handles waiting for all promises to finish. The tasks for them are already fired and being processed. Furthermore, your measurement is really flawed - the second loop doesn't do anything at all. If the promises are all resolved then awaiting them sequentially is not measuring anything useful. Mostly the dequeueing of noops from the microtask queue. – VLAZ Apr 28 '22 at 13:33
  • 1
    @Wyck I meant to comment out one block and run the code, so one block gets executed at a time.... I edited the question now! – shubham Apr 28 '22 at 14:34

1 Answers1

2

Imagine ordering 3 packages from Amazon. Your orders are analogous to "promises" that you can wait for.

What you are contemplating with Promise.all(promises) vs for (p of promises) await p; can be adapted to this analogy.

Promise.all is like saying "Wait until all my packages have arrived." It doesn't matter what order they arrive in, but your waiting concludes when the final package arrives -- in this case, when you have all 3 packages.

for-await is like waiting for them individually: "Wait until my headphones arrive, then wait until my pillow arrives, then wait until my can-opener arrives." In this case, if the can-opener arrives first, then you spend most of your time waiting for the headphones, and when it comes time to wait for the can-opener, it may have already arrived or there may be a bit more time to wait for it. In fact, it may have arrived before the headphones, you just weren't explicitly waiting for that particular package at that moment. It may very well be sitting on your porch already. If the can-opener is on back-order but the headphones arrived next day then you will spend most of your time waiting for the can-opener, and the headphones and pillow may have already arrived long ago. You aren't done waiting for everything until that last package arrives though.

So the overall duration is how long it takes the longest delivery to arrive.

It's critically important to note that the order in which you wait for your deliveries has nothing to do with how long it takes for the deliveries to arrive. You can't make your Amazon orders arrive more quickly or more slowly by adjusting the order of which package you hope will arrive next. You can sit by your front door wishfully waiting for only the headphones, effectively ignoring if and when the delivery person drops off your pillow or can-opener. What you were hoping for doesn't affect what package arrives first or how long it takes.

Promise.all could correctly be implemented internally with your for-await loop. So it shouldn't be surprising that they take the same amount of time.


Or consider conceiving a baby and ordering a pizza on the same day. This will take about nine months until you can say both have been delivered. If you wait for only the baby first, your pizza will be...quite cold, that's all.

Wyck
  • 10,311
  • 6
  • 39
  • 60
  • As commented above by @VLAZ, `Promise.all()` don't send anything, it only handles waiting for all promises to finish. The task for them are already fired and being processed. if this is correct then all tasks in the array are not fired at once?? Like we use `Promise.all()` in browsers to send multiple http requests at once and wait till all gets resolved or one gets rejected. Similarly why all `save()` requests are sent at once to `MongoDB`?? – shubham Apr 29 '22 at 02:50
  • You use Promise.all to wait for the completion of the requests. You have to create a promise first. The request is initiated when you do your `fetch` or whatever returns the promise. You push the promises into an array and then call Promise.all on them to be awakened once all the promises have resolved. Promise.all doesn't "send" anything. the `fetch` or in your case `save` is the thing that starts some kind of asynchronous action in motion. Promise.all just turns n promises into a single promise that can be awaited, and that promise completes when all n promises have completed. – Wyck Apr 29 '22 at 03:56
  • Got it! Unlike `fetch()` _mongoose's_ `Model.save()` don't send the _save_ request to the database until we await it or we pass _callbacks_ to some functions like `Model.update({})` ! Thanks @Wyck, Your gave an elegant example. Much appreciated! – shubham Apr 29 '22 at 04:43
  • 2
    @shubham. Actually your last comment sounds quite incorrect. When you do `promise = Model.save()` the I/O operations to begin the save **do** commence. The wheels are set in motion at that point, it's just that the database hasn't finished yet, but the saving is definitely in progress already at that point, and even if it was finished, it couldn't invoke a callback until you relinquish script control to the IO subsystem (with `await`). When you do `await promise`, that just means, "please continue executing this script when the operation associated with that promise completes". – Wyck Apr 29 '22 at 12:59