Skip to content

Instantly share code, notes, and snippets.

@victorquinn
Last active March 30, 2023 04:29
Show Gist options
  • Save victorquinn/8030190 to your computer and use it in GitHub Desktop.
Save victorquinn/8030190 to your computer and use it in GitHub Desktop.
Promise "loop" using the Bluebird library
var Promise = require('bluebird');
var promiseWhile = function(condition, action) {
var resolver = Promise.defer();
var loop = function() {
if (!condition()) return resolver.resolve();
return Promise.cast(action())
.then(loop)
.catch(resolver.reject);
};
process.nextTick(loop);
return resolver.promise;
};
// And below is a sample usage of this promiseWhile function
var sum = 0,
stop = 10;
promiseWhile(function() {
// Condition for stopping
return sum < stop;
}, function() {
// The function to run, should return a promise
return new Promise(function(resolve, reject) {
// Arbitrary 250ms async method to simulate async process
setTimeout(function() {
sum++;
// Print out the sum thus far to show progress
console.log(sum);
resolve();
}, 250);
});
}).then(function() {
// Notice we can chain it because it's a Promise, this will run after completion of the promiseWhile Promise!
console.log("Done");
});
@mklbtz
Copy link

mklbtz commented Jan 27, 2016

For anyone who doesn't want to use process.nextTick(), I've written my own solution here. I'm using the Q framework, but it could be very easily reworked to use Bluebird. It returns a promise which will resolve to the final iteration's return value, so it works a lot like reduce.

function promiseUntil(correct, action, last) {
  if (last === undefined || last.then === undefined) { last = Q(last); }
  return last.then(action).then(function (v) {
    if (correct(v)) {
      return Q(v);
    } else {
      return promiseUntil(correct, action, Q(v));
    }
  });
}

That is the promise-y equivalent of this function:

function loopUntil(correct, action, last) {
  var value = action(last);
  if (correct(value)) {
    return value;
  } else {
    return loopUntil(correct, action, value);
  }
}

Usage is straightforward:

function equalFour(v) {
  return v === 4;
}

function increment(v) {
  console.log(v);
  return (v === undefined) ? 0 : v+1;
}

var p = promiseUntil(equalFour, increment);
// prints:
// undefined
// 0
// 1
// 2
// 3

p = promiseUntil(equalFour, increment, 0);
// prints:
// 0
// 1
// 2
// 3

console.log(p);
// { state: 'fulfilled', value: 4 }

@ejc3
Copy link

ejc3 commented May 5, 2016

@mkbitz, won't your solution run out of memory unless the JS engine supports tail call optimization?

@lktvlm
Copy link

lktvlm commented May 19, 2016

@petkaantonov your version crashes in chrome with RangeError: Maximum call stack size exceeded.

Still looking for and elegant alternative for bluebird that does actually work...

@alexcorvi
Copy link

ES6 native implementation

        var promiseWhile = function(condition, action) {
            var resolver = Promise.defer();
            var loop = function() {
                if (!condition()) return resolver.resolve();
                return new Promise(action)
                    .then(loop)
                    .catch(resolver.reject);
            };
            process.nextTick(loop);
            return resolver.promise;
        };


        var sum = 0, stop = 10;

        promiseWhile(function() {
            return sum < stop;
        },function(resolve,reject){
            setTimeout(function() {
                sum++;
                console.log(sum);
                resolve();
            }, 250);
        }).then(function() {
            console.log("Done");
        });

@alexcorvi
Copy link

alexcorvi commented Sep 4, 2016

For loop in an array

promiseFor = Promise.method(function(arr,action,steps) {
    "use strict";
    if(!steps) steps = 0;
    if(arr.length<=steps) return;
    return new Promise((resolve,reject)=>{
        try {
            action(steps);
            resolve();
         }
         catch(e) {
             reject(e);
         }
    }).then(Promise.for.bind(null,arr,action,steps+1));
});

Usage

asyncFunc()
.then(()=>{
    var arr = [1,2,3]
    return new promiseFor(arr,(i)=>{ console.log(i) });
})
.then(()=>{
    console.log("done iterating");
});

@alexcorvi
Copy link

Doing async function on each members of the array

global.Promise.asyncOnEach = Promise.method(function(arr,action,steps) {
    if(!steps) steps = 0;
    console.log("Currently working on index:",steps,"Value:",arr[steps]);
    if(arr.length<=steps) {
        console.log("Finished working on the array, now will return it");
        console.log(arr);
        return arr;
    }
    return new Promise((resolve,reject)=>{
        action(arr[steps]).then((v)=>{
            arr[steps] = v;
            console.log(v);
            resolve(arr);
        }).catch((e)=>{
            reject(e);
        });
    }).then(Promise.asyncOnEach.bind(null,arr,action,steps+1));
});

Usage:

Promise.asyncOnEach([3,6,9],asyncFunc)
.then((arr)=>{
    console.log("The result is:",arr);
    res.send("OK");
}).catch((e)=>{
    console.log(e);
    res.send("error");
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment