简体   繁体   English

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

[英]Proper while() loop for bluebird promises (without recursion?)

I've been learning promises using bluebird for two weeks now. 我一直在学习使用蓝鸟两周的承诺。 I have them mostly understood, but I went to go solve a few related problems and it seems my knowledge has fell apart. 我对他们大多了解,但我去解决一些相关的问题,似乎我的知识已经崩溃了。 I'm trying to do this simple code: 我正在尝试这个简单的代码:

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

as a concrete example: 作为一个具体的例子:

// 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!");
});

Now I've done my homework! 现在我完成了我的作业! There is the same question, but geared toward Q.js here: 有同样的问题,但在这里面向Q.js:

Correct way to write loops for promise. 正确的写入循环的方法。

But the accepted answers, as well as additional answers: 但是接受的答案以及其他答案:

  • Are geared toward Q.js or RSVP 面向Q.js或RSVP
  • The only answer geared toward bluebird uses recursion. 面向蓝鸟的唯一答案是使用递归。 These seems like it's likely to cause a huge stack overflow in an infinite loop such as mine? 这些似乎很可能在我的无限循环中导致巨大的堆栈溢出? Or at best, be very inefficient and create a very large stack for nothing? 或者充其量,效率非常低并且无需创建非常大的堆栈? If I'm wrong, then fine! 如果我错了,那很好! Let me know. 让我知道。
  • Don't allow you to use result of the condition. 不允许您使用条件的结果。 Although this isn't requirement -- I'm just curious if it's possible. 虽然这不是必需的 - 如果可能,我只是好奇。 The code I'm writing, one use case needs it, the other doesn't. 我正在编写的代码,一个用例需要它,另一个不需要。

Now, there is an answer regarding RSVP that uses this async() method. 现在, 关于使用此异步()方法RSVP答案。 And what really confuses me is bluebird documents and I even see code for a Promise.async() call in the repository, but I don't see it in my latest copy of bluebird. 真正令我困惑的是bluebird文档,我甚至在存储库中看到了Promise.async()调用的代码,但我在最新的bluebird副本中看不到它。 Is it in the git repository only or something? 它只在git存储库中还是什么?

It's not 100% clear what you're trying to do, but I'll write an answer that does the following things you mention: 这并不是100%清楚你想要做什么,但我会写一个答案来做你提到的以下事情:

  1. Loops until some condition in your code is met 循环直到满足代码中的某些条件
  2. Allows you to use a delay between loop iterations 允许您在循环迭代之间使用延迟
  3. Allows you to get and process the final result 允许您获取并处理最终结果
  4. Works with Bluebird (I'll code to the ES6 promise standard which will work with Bluebird or native promises) 与Bluebird一起使用(我将代码符合ES6承诺标准,该标准将与Bluebird或本机承诺一起使用)
  5. Does not have stack build-up 没有堆栈累积

First, let's assume you have some async function that returns a promise whose result is used to determine whether to continue looping or not. 首先,假设您有一些异步函数返回一个promise,其结果用于确定是否继续循环。

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

Now, you want to loop until the value returned meets some condition 现在,您希望循环,直到返回的值满足某些条件

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

If you wanted to make this more generic so you could pass in the function and comparison, you could do this: 如果你想让它更通用,那么你可以传递函数和比较,你可以这样做:

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

Your attempts at a structure like this: 您尝试这样的结构:

return getNextItem() !== false;

Can't work because getNextItem() returns a promise which is always !== false since a promise is an object so that can't work. 无法工作,因为getNextItem()返回一个始终为!== false的promise,因为promise是一个对象,因此无法工作。 If you want to test a promise, you have to use .then() to get its value and you have to do the comparson asynchronously so you can't directly return a value like that. 如果你想测试一个promise,你必须使用.then()来获取它的值,你必须异步执行comparson所以你不能直接返回这样的值。


Note: While these implementations use a function that calls itself, this does not cause stack build-up because they call themselves asynchronously. 注意:虽然这些实现使用调用自身的函数,但这不会导致堆栈堆积,因为它们会异步调用自身。 That means the stack has already completely unwound before the function calls itself again, thus there is no stack build-up. 这意味着在函数再次调用自身之前,堆栈已经完全展开,因此没有堆栈构建。 This will always be the case from a .then() handler since the Promise specification requires that a .then() handler is not called until the stack has returned to "platform code" which means it has unwound all regular "user code" before calling the .then() handler. 这将始终是.then()处理程序的情况,因为Promise规范要求在堆栈返回“平台代码”之前不调用.then()处理程序,这意味着它已经解开所有常规“用户代码”之前调用.then()处理程序。


Using async and await in ES7 在ES7中使用asyncawait

In ES7, you can use async and await to "pause" a loop. 在ES7中,您可以使用async并等待“暂停”循环。 That can make this type of iteration a lot simpler to code. 这可以使这种类型的迭代更容易编码。 This looks structurally more like a typical synchronous loop. 这在结构上看起来更像是典型的同步循环。 It uses await to wait on promises and because the function is declared async , it always returns a promise: 它使用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
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM