Whats the best approach to use q promise in a loop

2019-07-08 03:03发布

问题:

I have the following situation:

var ids = [120, 121, 122, 123, 124]

function dummyPromise(pause) {
  var deferred = Q.defer();

  setTimeout(function() {
    console.log(pause);
    deferred.resolve(pause);
  }, pause);

  return deferred.promise;
}


for(var i = 0; i < ids.length; i++) {
  dummyPromise(ids[i])
    .then(dummyPromise)
    .then(dummyPromise)
    .then(dummyPromise)
    .then(dummyPromise)
    .done(function(){
      console.log('done')
    })
}

I want to wait for chains to complete before iterating to next. Whats the best way to approach this?

回答1:

In these examples I use standard Promises. If you need to use (the awesome) Q library, you can make it shorter by substituting Q.fcall(fn, arg) in places of Promise.resolve(arg).then(fn) or using Q() instead of Promise.resolve().

By chaining promises

var q = Promise.resolve();

for (var i = 0; i < 10; i++) {
  (function(item, index){
    q = q.then(function() {
      return // do async stuff here
    });
  })(ids[i], i);
}

q.then(function() {
  // all iterations finished
});

With arrays

function forEachAsync(arr, fn) {
  var index = 0;
  function next() {
    if (index < arr.length) {
      var current = index++;
      return Promise.resolve().then(function() {
        return fn(arr[current], current, arr);
      }).then(next);
    }
  }
  return Promise.resolve().then(next);
}

...

forEachAsync(ids, function(item, idx) { ... }).then(...)

With iterables

function forOfAsync(iterable, fn) {
  var iterator = iterable[Symbol.iterator]();
  function next() {
    var iteration = iterator.next();
    if (iteration.done) {
      return iteration.value;
    } else {
      return Promise.resolve(iteration.value).then(fn).then(next);
    }
  }
  return Promise.resolve().then(next);
}

forOfAsync(ids, function(id) { ... }).then(...)

With async-await

for (let id of ids) {
  await doSomeAsyncStuffWithId(id);
}


回答2:

Using array#reduce - your code would look like

ids.reduce(function(prev, id) {
    return prev
    .then(function() {
        return dummyPromise(id)
    })
    .then(dummyPromise)
    .then(dummyPromise)
    .then(dummyPromise)
    .then(dummyPromise)
    .then(function(){ // if you do done you can't chain
      console.log('done')
    });
}, Q(null))
.done(function() { // done here though
    console.log('all done');
});


回答3:

Thanks to https://github.com/jprichardson/node-batchflow . I ended using this approach: See fiddle: https://jsfiddle.net/c9fxqhs5/

  var ids = [120, 121, 122, 123, 124]

  function dummyPromise(pause) {
    var deferred = Q.defer();

    setTimeout(function() {
      console.log(pause);
      deferred.resolve(pause);
    }, pause);

    return deferred.promise;
  }

  function again(i) {
    if (i < ids.length) {
      $('body').append('<p>processing: ' + i + '</p>');
      dummyPromise(ids[i])
        .then(dummyPromise)
        .then(dummyPromise)
        .then(dummyPromise)
        .then(dummyPromise)
        .done(function(result){
          $('body').append('<p>done: ' + i + ' result=' + result + '</p>');
          again(i+1)
        })
    } else {
      console.log('All Done.')
    }
  }

  again(0)