Correct way to write loops for promise.

2018-12-31 17:52发布

How to correctly construct a loop to make sure the following promise call and the chained logger.log(res) runs synchronously through iteration? (bluebird)

db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise

I tried the following way (method from http://blog.victorquinn.com/javascript-promise-while-loop )

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;
});

var count = 0;
promiseWhile(function() {
    return count < 10;
}, function() {
    return new Promise(function(resolve, reject) {
        db.getUser(email)
          .then(function(res) { 
              logger.log(res); 
              count++;
              resolve();
          });
    }); 
}).then(function() {
    console.log('all done');
}); 

Although it seems to work, but I don't think it guarantees the order of calling logger.log(res);

Any suggestions?

13条回答
回忆,回不去的记忆
2楼-- · 2018-12-31 18:26

First take array of promises(promise array) and after resolve these promise array using Promise.all(promisearray).

var arry=['raju','ram','abdul','kruthika'];

var promiseArry=[];
for(var i=0;i<arry.length;i++) {
  promiseArry.push(dbFechFun(arry[i]));
}

Promise.all(promiseArry)
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
     console.log(error);
  });

function dbFetchFun(name) {
  // we need to return a  promise
  return db.find({name:name}); // any db operation we can write hear
}
查看更多
梦醉为红颜
3楼-- · 2018-12-31 18:29

How about this one using BlueBird?

function fetchUserDetails(arr) {
    return Promise.each(arr, function(email) {
        return db.getUser(email).done(function(res) {
            logger.log(res);
        });
    });
}
查看更多
谁念西风独自凉
4楼-- · 2018-12-31 18:30

Bergi's suggested function is really nice:

var promiseWhile = Promise.method(function(condition, action) {
      if (!condition()) return;
    return action().then(promiseWhile.bind(null, condition, action));
});

Still I want to make a tiny addition, which makes sense, when using promises:

var promiseWhile = Promise.method(function(condition, action, lastValue) {
  if (!condition()) return lastValue;
  return action().then(promiseWhile.bind(null, condition, action));
});

This way the while loop can be embedded into a promise chain and resolves with lastValue (also if the action() is never run). See example:

var count = 10;
util.promiseWhile(
  function condition() {
    return count > 0;
  },
  function action() {
    return new Promise(function(resolve, reject) {
      count = count - 1;
      resolve(count)
    })
  },
  count)
查看更多
墨雨无痕
5楼-- · 2018-12-31 18:31

Here's another method (ES6 w/std Promise). Uses lodash/underscore type exit criteria (return === false). Note that you could easily add an exitIf() method in options to run in doOne().

const whilePromise = (fnReturningPromise,options = {}) => { 
    // loop until fnReturningPromise() === false
    // options.delay - setTimeout ms (set to 0 for 1 tick to make non-blocking)
    return new Promise((resolve,reject) => {
        const doOne = () => {
            fnReturningPromise()
            .then((...args) => {
                if (args.length && args[0] === false) {
                    resolve(...args);
                } else {
                    iterate();
                }
            })
        };
        const iterate = () => {
            if (options.delay !== undefined) {
                setTimeout(doOne,options.delay);
            } else {
                doOne();
            }
        }
        Promise.resolve()
        .then(iterate)
        .catch(reject)
    })
};
查看更多
回忆,回不去的记忆
6楼-- · 2018-12-31 18:35
function promiseLoop(promiseFunc, paramsGetter, conditionChecker, eachFunc, delay) {
    function callNext() {
        return promiseFunc.apply(null, paramsGetter())
            .then(eachFunc)
    }

    function loop(promise, fn) {
        if (delay) {
            return new Promise(function(resolve) {
                setTimeout(function() {
                    resolve();
                }, delay);
            })
                .then(function() {
                    return promise
                        .then(fn)
                        .then(function(condition) {
                            if (!condition) {
                                return true;
                            }
                            return loop(callNext(), fn)
                        })
                });
        }
        return promise
            .then(fn)
            .then(function(condition) {
                if (!condition) {
                    return true;
                }
                return loop(callNext(), fn)
            })
    }

    return loop(callNext(), conditionChecker);
}


function makeRequest(param) {
    return new Promise(function(resolve, reject) {
        var req = https.request(function(res) {
            var data = '';
            res.on('data', function (chunk) {
                data += chunk;
            });
            res.on('end', function () {
                resolve(data);
            });
        });
        req.on('error', function(e) {
            reject(e);
        });
        req.write(param);
        req.end();
    })
}

function getSomething() {
    var param = 0;

    var limit = 10;

    var results = [];

    function paramGetter() {
        return [param];
    }
    function conditionChecker() {
        return param <= limit;
    }
    function callback(result) {
        results.push(result);
        param++;
    }

    return promiseLoop(makeRequest, paramGetter, conditionChecker, callback)
        .then(function() {
            return results;
        });
}

getSomething().then(function(res) {
    console.log('results', res);
}).catch(function(err) {
    console.log('some error along the way', err);
});
查看更多
笑指拈花
7楼-- · 2018-12-31 18:35

Using the standard promise object, and having the promise return the results.

function promiseMap (data, f) {
  const reducer = (promise, x) =>
    promise.then(acc => f(x).then(y => acc.push(y) && acc))
  return data.reduce(reducer, Promise.resolve([]))
}

var emails = []

function getUser(email) {
  return db.getUser(email)
}

promiseMap(emails, getUser).then(emails => {
  console.log(emails)
})
查看更多
登录 后发表回答