Sequential promise loop with delay

2020-03-31 04:58发布

问题:

I'm attempting to load an array of "requests" sequentially each one separated by a delay.

I'm using promises but for some reasons I'm having issues with the requests being executed in parallel instead of in sequence.

I wrote some test code as per below. It works! It makes the request, processes it, timesOut for 3 second and goes to the second request.

var batches = [of_objects];
var sequence = Promise.resolve();
var self = this;

sequence
    //Create request for first batch
    .then( function(){
        return self._createRequestPromise(batches[0]);
    })
    //callback so we can update, returns nothing
    .then(callback)
    //add 3 sec timeout before next request
    .then(function(){
        return new Promise(function (resolve, reject){
            setTimeout(resolve, 3000);
        });
    })
    //Create request for second batch
    .then( function(){
        return self._createRequestPromise(batches[1]);
    })
    //callback so we can update, returns nothing
    .then(callback)
    //add 3 sec timeout before next request
    .then(function(){
        return new Promise(function (resolve, reject){
            setTimeout(resolve, 3000);
        });
    });

return sequence;

BUT, as soon as I try to put it any sort of loop, my requests are firing all at the same time. And the timeout calls happen after.

I'm not sure what I'm doing wrong, or misunderstanding.

//object I need to use to construct requests
var batches = [of_objects];
var sequence = Promise.resolve();
var self = this;

batches.forEach(function(batch){
    sequence
    //Function returns promise
    .then( function(){
        return self._createRequestPromise(batch); //this runs first 5x
    })
    //callback so we can update, returns nothing
    .then(callback)
    //add 3 sec timeout before next request
    .then(function(){
        return new Promise(function (resolve, reject){
            setTimeout(resolve, 3000); //this runs after 5x
        });
    });
});
return sequence;

回答1:

The issue is that you're not updating sequence to include any of the successive operations, so all of them are chaining off of that original resolved promise. There's nothing there to delay any of them, so they execute right away.

You should be able to remedy this simply by updating the sequence variable on every loop, so you're chaining off the end of the chain:

//object I need to use to construct requests
var batches = [of_objects];
var sequence = Promise.resolve();
var self = this;

batches.forEach(function (batch){
    sequence = sequence            // <-- here
        //Function returns promise
        .then( function(){
            return self._createRequestPromise(batch); //this runs first 5x
        })
        //callback so we can update, returns nothing
        .then(callback)
        //add 3 sec timeout before next request
        .then(function(){
            return new Promise(function (resolve, reject){
                setTimeout(resolve, 3000); //this runs after 5x
            });
        });
});

return sequence;

Personally, I try to avoid overwriting variables, so an alternate way to do this without variable reassignment is to use .reduce():

//object I need to use to construct requests
var batches = [of_objects];
var self = this;

return batches.reduce(function (last, batch){
    return last
        //Function returns promise
        .then( function(){
            return self._createRequestPromise(batch); //this runs first 5x
        })
        //callback so we can update, returns nothing
        .then(callback)
        //add 3 sec timeout before next request
        .then(function(){
            return new Promise(function (resolve, reject){
                setTimeout(resolve, 3000); //this runs after 5x
            });
        });
}, Promise.resolve());