蓝鸟承诺的正确while()循环(没有递归?)

时间:2016-05-01 21:20:24

标签: javascript promise bluebird

我一直在学习使用蓝鸟两周的承诺。我对他们大多了解,但我去解决一些相关的问题,似乎我的知识已经崩溃了。我正在尝试这么简单的代码:

var someGlobal = true;

whilePromsie(function() { 
   return someGlobal;
}, function(result) { // possibly even use return value of 1st parm?
 // keep running this promise code
 return new Promise(....).then(....);
});

作为一个具体的例子:

// This is some very contrived functionality, but let's pretend this is 
// doing something external: ajax call, db call, filesystem call, etc.
// Simply return a number between  0-999 after a 0-999 millisecond
// fake delay.
function getNextItem() { 
    return new Promise.delay(Math.random()*1000).then(function() {
        Promise.cast(Math.floor(Math.random() * 1000));
    });
}

promiseWhile(function() {
    // this will never return false in my example so run forever
    return getNextItem() !== false;
}, // how to have result == return value of getNextItem()? 
function(result) {
    result.then(function(x) { 
        // do some work ... 
    }).catch(function(err) { 
        console.warn("A nasty error occured!: ", err);
    });
}).then(function(result) { 
    console.log("The while finally ended!");
});

现在我完成了我的作业!有同样的问题,但是这里面向Q.js:

Correct way to write loops for promise.

但接受的答案以及其他答案:

  • 面向Q.js或RSVP
  • 面向蓝鸟的唯一答案是使用递归。这些似乎很可能在我的无限循环中导致巨大的堆栈溢出?或者充其量,效率非常低并且无需创建非常大的堆栈?如果我错了,那很好!请告诉我。
  • 不允许您使用条件的结果。虽然这不是必需的 - 如果可能,我只是好奇。我写的代码,一个用例需要它,另一个不需要。

现在, 是关于使用此async()方法的RSVP的答案。真正令我困惑的是bluebird文档,我甚至在存储库中看到了Promise.async()调用的代码,但我在最新的bluebird副本中看不到它。它只在git存储库中吗?

1 个答案:

答案 0 :(得分:13)

它不是100%清楚你正在尝试做什么,但我会写一个答案来做你提到的以下事情:

  1. 循环,直到满足代码中的某些条件
  2. 允许您在循环迭代之间使用延迟
  3. 允许您获取并处理最终结果
  4. 与Bluebird合作(我将代码编写为ES6承诺标准,该标准将与Bluebird或本机承诺一起使用)
  5. 没有堆叠构建
  6. 首先,让我们假设您有一些异步函数返回一个promise,其结果用于确定是否继续循环。

    function getNextItem() {
       return new Promise.delay(Math.random()*1000).then(function() {
            return(Math.floor(Math.random() * 1000));
       });
    }
    

    现在,您希望循环,直到返回的值满足某些条件

    function processLoop(delay) {
        return new Promise(function(resolve, reject) {
            var results = [];
    
            function next() {
                getNextItem().then(function(val) {
                    // add to result array
                    results.push(val);
                    if (val < 100) {
                        // found a val < 100, so be done with the loop
                        resolve(results);
                    } else {
                        // run another iteration of the loop after delay
                        setTimeout(next, delay);
                    }
                }, reject);
            }
            // start first iteration of the loop
            next();
        });
    }
    
    processLoop(100).then(function(results) {
       // process results here
    }, function(err) {
       // error here
    });
    

    如果你想让它更通用,那么你可以传递函数和比较,你可以这样做:

    function processLoop(mainFn, compareFn, delay) {
        return new Promise(function(resolve, reject) {
            var results = [];
    
            function next() {
                mainFn().then(function(val) {
                    // add to result array
                    results.push(val);
                    if (compareFn(val))
                        // found a val < 100, so be done with the loop
                        resolve(results);
                    } else {
                        // run another iteration of the loop after delay
                        if (delay) {
                            setTimeout(next, delay);
                        } else {
                            next();
                        }
                    }
                }, reject);
            }
            // start first iteration of the loop
            next();
        });
    }
    
    processLoop(getNextItem, function(val) {
        return val < 100;
    }, 100).then(function(results) {
       // process results here
    }, function(err) {
       // error here
    });
    

    您尝试这样的结构:

    return getNextItem() !== false;
    

    无法正常工作,因为getNextItem()会返回一个始终为!== false的承诺,因为承诺是一个对象,因此无法正常工作。如果你想测试一个promise,你必须使用.then()来获取它的值,你必须异步执行comparson,这样你才能直接返回一个这样的值。

    注意:虽然这些实现使用调用自身的函数,但这不会导致堆栈堆积,因为它们会异步调用自身。这意味着在函数再次调用自身之前,堆栈已经完全展开,因此没有堆栈构建。从.then()处理程序始终是这种情况,因为Promise规范要求在堆栈返回到平台代码&#34;之前不调用.then()处理程序。这意味着它已经解开了所有常规用户代码&#34;在调用.then()处理程序之前。

    在ES7中使用asyncawait

    在ES7中,您可以使用async并等待&#34;暂停&#34;一个循环。这可以使这种类型的迭代更容易编码。这在结构上看起来更像是典型的同步循环。它使用await来等待promises,因为函数声明为async,它总是返回一个promise:

    function delay(t) {
        return new Promise(resolve => {
            setTimeout(resolve, t);
        });
    }
    
    async function processLoop(mainFn, compareFn, timeDelay) {
        var results = [];
    
        // loop until condition is met
        while (true) {
            let val = await mainFn();
            results.push(val);
            if (compareFn(val)) {
                return results;
            } else {
                if (timeDelay) {
                    await delay(timeDelay);
                }
            }
        }
    }
    
    processLoop(getNextItem, function(val) {
        return val < 100;
    }, 100).then(function(results) {
       // process results here
    }, function(err) {
       // error here
    });