While loop with promises While loop with promises node.js node.js

While loop with promises


Here's a reusable function that I think is pretty clear.

var Q = require("q");// `condition` is a function that returns a boolean// `body` is a function that returns a promise// returns a promise for the completion of the loopfunction promiseWhile(condition, body) {    var done = Q.defer();    function loop() {        // When the result of calling `condition` is no longer true, we are        // done.        if (!condition()) return done.resolve();        // Use `when`, in case `body` does not return a promise.        // When it completes loop again otherwise, if it fails, reject the        // done promise        Q.when(body(), loop, done.reject);    }    // Start running the loop in the next tick so that this function is    // completely async. It would be unexpected if `body` was called    // synchronously the first time.    Q.nextTick(loop);    // The promise    return done.promise;}// Usagevar index = 1;promiseWhile(function () { return index <= 11; }, function () {    console.log(index);    index++;    return Q.delay(500); // arbitrary async}).then(function () {    console.log("done");}).done();


This is the simplest way I've found to express the basic pattern: you define a function that calls the promise, checks its result, and then either calls itself again or terminates.

const doSomething = value =>  new Promise(resolve =>     setTimeout(() => resolve(value >= 5 ? 'ok': 'no'), 1000))const loop = value =>  doSomething(value).then(result => {    console.log(value)    if (result === 'ok') {      console.log('yay')          } else {      return loop(value + 1)    }  })loop(1).then(() => console.log('all done!'))

See it in action on JSBin

If you were using a promise that resolves or rejects, you would define then and catch instead of using an if-clause.

If you had an array of promises, you would just change loop to shift or pop the next one each time.


EDIT: Here's a version that uses async/await, because it's 2018:

const loop = async value => {  let result = null  while (result != 'ok') {    console.log(value)    result = await doSomething(value)    value = value + 1  }  console.log('yay')}

See it in action on CodePen

As you can see, it uses a normal while loop and no recursion.


I'd use an object to wrap the value. That way you can have a done property to let the loop know you're done.

// fn should return an object like// {//   done: false,//   value: foo// }function loop(promise, fn) {  return promise.then(fn).then(function (wrapper) {    return !wrapper.done ? loop(Q(wrapper.value), fn) : wrapper.value;  });}loop(Q.resolve(1), function (i) {  console.log(i);  return {    done: i > 10,    value: i++  };}).done(function () {  console.log('done');});