同时循环与承诺

Gru*_*mle 69 loops node.js promise q

什么是惯用的方式做一些像承诺的while循环.所以:

如果条件仍然有效,请再做一次然后再做其他事情.

dosomething.then(possilblydomoresomethings).then(finish)
Run Code Online (Sandbox Code Playgroud)

我是这样做的,我想知道是否有更好/更多的自我方式?

var q = require('q');

var index = 1;

var useless =  function(){
        var currentIndex = index;
        console.log(currentIndex)
        var deferred = q.defer();
        setTimeout(function(){
            if(currentIndex > 10)
                deferred.resolve(false);
            else deferred.resolve(true);
            },500);
        return deferred.promise;
    }

var control = function(cont){
        var deferred = q.defer();
        if(cont){
                index = index + 1;
                useless().then(control).then(function(){
                        deferred.resolve();
                    });
            }
         else deferred.resolve();
        return deferred.promise;
    }

var chain = useless().then(control).then(function(){console.log('done')});
Run Code Online (Sandbox Code Playgroud)

输出:1 2 3 4 5 6 7 8 9 10 11完成

Stu*_*t K 58

这是一个可重用的功能,我认为非常清楚.

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 loop
function 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;
}


// Usage
var index = 1;
promiseWhile(function () { return index <= 11; }, function () {
    console.log(index);
    index++;
    return Q.delay(500); // arbitrary async
}).then(function () {
    console.log("done");
}).done();
Run Code Online (Sandbox Code Playgroud)

  • 新版本更多RSVP惯用和身体和条件的Promise"包装":http://jsfiddle.net/wcW4r/3/ (5认同)

law*_*nce 28

这是我发现表达基本模式的最简单方法:定义一个调用promise的函数,检查其结果,然后再次调用自身或终止.

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!'))
Run Code Online (Sandbox Code Playgroud)

在JSBin上查看它的实际操作

如果您使用的是解析或拒绝的承诺,您将定义thencatch不是使用if子句.

如果您有一系列承诺,您只需更改loop为每次移动或弹出下一个.


编辑:这是一个使用的版本async/await,因为它是2018年:

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

在CodePen上查看它的实际操作

如您所见,它使用正常的while循环而不是递归.

  • 非常好的解决方案!我对此进行了修改,以将另一个变量通过方法链传递给每个promise。感谢您提供一个简单的示例! (2认同)
  • 比其他答案更有帮助 (2认同)

jua*_*azo 19

我用一个对象来包装值.这样你就可以拥有一个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');
});
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在运行足够长的时间后,这可能会消耗所有可用内存; 似乎在承诺的每个循环中都保留了一些东西,至少在Q. (9认同)
  • @juandopazo在你的例子中你应该用`++ i`改变`i ++`否则你会得到一个"无限循环". (4认同)

aar*_*sil 12

这是蓝鸟而不是q但是因为你没有特别提到q ..在bluebird api doc中,作者提到返回一个承诺生成函数比使用deferreds更惯用.

var Promise = require('bluebird');
var i = 0;

var counter = Promise.method(function(){
    return i++;
})

function getAll(max, results){
    var results = results || [];
    return counter().then(function(result){
        results.push(result);
        return (result < max) ? getAll(max, results) : results
    })
}

getAll(10).then(function(data){
    console.log(data);
})
Run Code Online (Sandbox Code Playgroud)


小智 5

由于我不能评论Stuart K的答案,我会在这里补充一点.根据Stuart K的回答,您可以将其归结为一个非常简单的概念:重用未实现的承诺.他拥有的基本上是:

  1. 创建延期保证的新实例
  2. 定义要在循环中调用的函数
  3. 在这个功能里面:
    1. 检查一下你是否完成了; 当你解决在#1中创建的承诺并将其返回时.
    2. 如果你没有完成,那么告诉Q使用现有的promise并运行unfullfilled函数,它是"递归"函数,如果它死了则失败.Q.when(promise,yourFunction,failFunction)
  4. 定义函数后,使用Q首次使用Q.nextTick(yourFunction)触发函数
  5. 最后将新的承诺返回给调用者(这将触发整个事情开始).

Stuart的答案是提供更通用的解决方案,但基础知识非常棒(一旦你意识到它是如何工作的).