JavaScript Serial Promises with setTimeout - javascript

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.

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).

Need help understanding Promise.all and async

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.

Add a delay or sleep to an array of promises in nodejs [duplicate]

This question already has answers here:
How to synchronize a sequence of promises?
(6 answers)
Closed 5 years ago.
I have a promise chain problem I have been grappling with. I make a call out to an external api that returns me data that I need to process and ingest into a mongo database. I am using nodejs and mongodb with express. Anyway, the calls to the api are working ok, the problem is that I am making tons of them at once. I want to slow them down, like make all the calls for one set. Wait a minute. Make all the calls for the next set. If this was a known amount of sets I would just promise chain them. I dont know how many sets there are so I am looping through them. I think the closure is the problem but cant work around it. on to the example code!
function readApi(carFactory){
var promise = new Promise(function(resolve, reject) {
// call out to api, get set of car data from factory1
console.log(carFactory);
if (true) {
console.log('resolved');
resolve("Stuff worked!"+carFactory);
}
else {
reject(Error("It broke"));
}
});
return promise;
}
function manager(){
//singular call
readApi("this is a singular test").then(returnedThing=>{
console.log(returnedThing); //Stuff worked! this is a singular test
});
let dynamicList = ["carFactory1", "carFactory2","carFactory3","carFactory..N"];
let readApiAction = [];
dynamicList.forEach(carIter=>{
readApiAction.push(readApi(carIter));
});
//ok so now I got an array of promise objects.
//I want to call the first one, wait 1 minute and then call the next one.
//originally I was calling promise.all, but there is no way to get at
//each promise to pause them out so this code is what I am looking to fix
let results= Promise.all(readApiAction);
results.then(data=>{
data.forEach(resolvedData=>{
console.log(resolvedData); //Stuff worked carFactory1, etc...
});
});
//singular call with timeout, this does not work, each one called at the same time
let readApiActionTimeouts = [];
dynamicList.forEach(carIter=>{
setTimeout(callingTimeout(carIter), 6000);
});
}
//just a function to call the promise in a timeout
//this fails with this - TypeError: "callback" argument must be a function
function callingTimeout(carIter){
readApi(carIter).then(returnedThing=>{
console.log("timeout version"+returnedThing);
});
}
A bit on theory. Native Promise.all just groups promises. They're still executed simultaneously (in async way, though, as all JS code, but along each other). This means that it will still congest the API and perform a lot of calls.
Another thing to note is that if you want to delay a promise, you have to delay its return value (e.g. resolve). In order to do so, you may use setTimeout INSIDE new Promise (just look below for more explanation).
Set timeout is asynchronous. It does not work as in other languages (it does not just pause execution). Setting fixed timeout in your code just caused to move ALL the executions by 6s. They still happened in parallel (in different ticks, though, but it's a small difference). Try e.g. generating different timeouts for each one in the loop - you'll notice that they're happening in a different time BUT! This is not a good solution for promisified code!
And now - time for practical answer!
If you use Bluebird, it has a special method to add delay or timeout to each promise. Without it, you would have to write a wrapper around Promise, e.g. resolving it after a particular amount of time and then use it with Promise.all.
First solution (bluebird):
function delayMyPromise(myPromise, myDelay);
return Promise.delay(myDelay).then(function() {
return myPromise;
});
});
and then in your code:
return Promise.all(delayMyPromise(promise1, 1000), delayMyPromise(promise2, 2000)); // Notice different delays, you may generate them programatically
Or even cooler, you can use Promise.map from Bluebird instead of Promise.all that has a special concurrency setting so you may force your promises to be executed in particular sequence, e.g. 2 at a time.
This is how I did it on my previous project :)
More here:
http://bluebirdjs.com/docs/api/promise.delay.html
http://bluebirdjs.com/docs/api/map.html
Pure native Promise implementation:
function delayMyPromise(myPromise, myDelay) {
return new Promise(function (resolve, reject) {
setTimeout(function() {
return resolve(myPromise);
}, myDelay);
});
}
I'd, however, heavily recommend first approach if you don't mind using Bluebird. It's like lodash for Promises and it's really fast :)
you get the error : TypeError: "callback" argument must be a function because your callingTimeout returns nothing and setTimeout needs a function as argument, this is how to fixe it :
let readApiActionTimeouts = [];
dynamicList.forEach(carIter=>{
callingTimeout(carIter)
});
your promise :
function readApi(carFactory){
var promise = new Promise(function(resolve, reject) {
//...
setTimeout(()=>{
resolve("Stuff worked!"+carFactory);
}, 6000);
//...
});
return promise;
}
You could use recursion for something like this.
When you call .forEach, each iteration happens immediately.
In the example below, doSomething is not called until the setTimeout occurs, which means each letter is printed 1 second apart.
let letters = ["a", "b", "c"];
function doSomething(arr) {
console.log(arr[0]);
if (arr.length > 1) {
setTimeout(() => doSomething(arr.slice(1)), 1000);
}
}
doSomething(letters);
Alternately, for your array of promises:
let promises = [
Promise.resolve("A"),
Promise.resolve("B"),
Promise.resolve("C"),
];
function doSomething(arr) {
arr[0].then((result) => {
console.log(result);
if (arr.length > 1) {
setTimeout(() => doSomething(arr.slice(1)), 1000);
}
})
}
doSomething(promises);

Does promise-chaining guarantee that a callback of the next promise is called only after preceding one is finished?

I met an article about promises in js, where the author shows a piece of code:
// I want to remove() all docs
db.allDocs({include_docs: true}).then(function (result) {
result.rows.forEach(function (row) {
db.remove(row.doc);
});
}).then(function () {
// I naively believe all docs have been removed() now!
});
and says
What's the problem with this code? The problem is that the first
function is actually returning undefined, meaning that the second
function isn't waiting for db.remove() to be called on all the
documents. In fact, it isn't waiting on anything, and can execute when
any number of docs have been removed!
So, as I understood, if the second callback function() {} accepts no arguments, it actually doesn't wait for the end of the callback function(result). Am I inferring correctly? Because when I run the following piece of code, it gives me an opposite result:
var array = [];
for ( let i = 0; i < 1000; i++ )
array.push(i);
var promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(), 1000);
});
promise
.then(() => array.forEach(item => console.log(item)))
.then(() => console.log("invoked earlier"));
I wait for "invoked earlier" to appear in the middle of printed numbers. But doesn't matter how large the number of items is. "Invoked earlier" appears always at the end. Can someone explain me what I am missing? Maybe the article is outdated and something has changed since then?
In order to guarantee this, you actually have to return promises from the previous promise.
Whatever you return from a promise will be passed into the next promise.
In your case, you're returning undefined.
If, instead, you returned a promise, then the promise would be resolved, and the second promise would run after that happened, and would be passed whatever value the promise resolved with.
So yes, promises are guaranteed to run one after another, but if you choose to do something async inside of their callback, and don't bother chaining it back into the promise by returning it, then they're not going to bother to wait for anything (because they don't know that there's anything to wait for).
I'm assuming db.remove returns a promise...
...so, knowing that we have to return a promise from the callback, in order for it to actually wait for async stuff to happen, I would do something like this.
.then(result => Promise.all(result.rows.map(row => db.remove(row.doc))))
.then((listOfRemoves) => console.log(`removed`));
Whether the second function takes 1 argument or 0 arguments is 100% inconsequential as to when the second function runs.
Edit
examples:
.then(() => setTimeout(() => console.log('2nd', 20000)))
.then(() => console.log('first'));
This happens because the first then has NO idea that there is a setTimeout happening. It's not a mind-reader, it just runs the code and passes the return value along (in this case, undefined).
If the return value happens to be a promise, though, it will wait until that promise is done, get the value from it, and pass that on.
.then(() => new Promise(resolve => setTimeout(resolve, 20000)))
.then(() => console.log('I waited 20 seconds.'));
Because it's returning a promise, it will call the then of that promise, and wait to get the value, to pass on.
The reason your tests are failing is because you're basically doing this.
.then(() => console.log('first'))
.then(() => console.log('second'));
These are guaranteed to fire in this order. Period.
All of the other ones have been firing in that order as well. It's just that they are also scheduling async processes, just like all other callbacks/timeouts that use async I/O in JS.

Q.all with chained sequence

I an using Q.js, and have a series of functions that return promises chained together with then callbacks, because, I need them to execute in order as a sequence.
var promiseOne = one();
promiseOne.then(two).then(three).done();
I also have some promise that can happen at any time. I need to know when they have ALL resolved - including the last then call back in the chain of promises. I was trying to use Q.all.then but it will reach Q.all's then before some of these promises -
Q.all([promiseOne, anotherPromise]).then(function(results) {
// will hit this before chain above is done
});
in my jsfiddle example, I just have the one promise in Q.all, but in reality it is one of many. Is there any way to make this work that that I can know when all, including chained then's, are resolved?
jsfiddle: http://jsfiddle.net/98rq0bs6/1/
A promise does not know what was chained to it. However, calling .then() does return a new promise which will know what it was chained from. You will need to take this new promise, which represents the result of the chained actions, and wait for it - instead of promiseOne, which was only the first link in the chain.
var promiseOne = one();
var promiseChain = promiseOne.then(two).then(three);
Q.all([promiseChain, anotherPromise]).then(function(results) {
// ^^^^^^^^^^^^ wait for the chain
…
}).done();
var listOfItems = [1, 2, 3, 4, 5];
// Creating an empty initial promise that always resolves itself.
var promise = $q.all([]);
// Iterating list of items.
angular.forEach(listOfItems, function (item) {
promise = promise.then(function () {
return $timeout(function () {
console.log('Item executed: ' + item);
}, 2000);
});
});
promise.finally(function () {
console.log('Chain finished!');
});
Source, In this solution, "angular.forEach" make possible that each promise be executed in chain, in other words, be a promises´s sequence

Categories

Resources