蓝鸟承诺的正确 while() 循环(没有递归?)
Proper while() loop for bluebird promises (without recursion?)
我已经使用 bluebird 学习 promises 两个星期了。我已经基本理解了,但是我去解决了一些相关的问题,看来我的知识已经崩溃了。我正在尝试执行这个简单的代码:
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 存储库中还是其他地方?
不是 100% 清楚你想做什么,但我会写一个答案来完成你提到的以下事情:
- 循环直到满足代码中的某些条件
- 允许您在循环迭代之间使用延迟
- 允许您获取并处理最终结果
- 与 Bluebird 一起工作(我将编码为 ES6 promise 标准,它将与 Bluebird 或本机 promises 一起工作)
- 没有堆栈建立
首先,假设您有一些异步函数,return是一个承诺,其结果用于确定是否继续循环。
function getNextItem() {
return new Promise.delay(Math.random()*1000).then(function() {
return(Math.floor(Math.random() * 1000));
});
}
现在,您想循环直到值 returned 满足某些条件
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()
return 始终是 !== false
的承诺,因为承诺是一个对象,因此无法工作。如果你想测试一个承诺,你必须使用 .then()
来获取它的值,你必须异步地进行比较,所以你不能直接 return 这样的值。
注意:虽然这些实现使用调用自身的函数,但这不会导致堆栈堆积,因为它们是异步调用自身的。这意味着在函数再次调用自身之前堆栈已经完全展开,因此没有堆栈堆积。 .then()
处理程序始终是这种情况,因为 Promise 规范要求 .then()
处理程序在堆栈 returned 到 "platform code" 之前不会被调用,这意味着它有在调用 .then()
处理程序之前解开所有常规 "user code"。
在 ES7 中使用 async
和 await
在 ES7 中,你可以使用 async 和 await 来 "pause" 一个循环。这可以使这种类型的迭代更易于编码。这在结构上看起来更像是一个典型的同步循环。它使用 await
来等待 promise,并且因为函数被声明为 async
,它总是 return 是一个 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
});
我已经使用 bluebird 学习 promises 两个星期了。我已经基本理解了,但是我去解决了一些相关的问题,看来我的知识已经崩溃了。我正在尝试执行这个简单的代码:
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 存储库中还是其他地方?
不是 100% 清楚你想做什么,但我会写一个答案来完成你提到的以下事情:
- 循环直到满足代码中的某些条件
- 允许您在循环迭代之间使用延迟
- 允许您获取并处理最终结果
- 与 Bluebird 一起工作(我将编码为 ES6 promise 标准,它将与 Bluebird 或本机 promises 一起工作)
- 没有堆栈建立
首先,假设您有一些异步函数,return是一个承诺,其结果用于确定是否继续循环。
function getNextItem() {
return new Promise.delay(Math.random()*1000).then(function() {
return(Math.floor(Math.random() * 1000));
});
}
现在,您想循环直到值 returned 满足某些条件
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()
return 始终是 !== false
的承诺,因为承诺是一个对象,因此无法工作。如果你想测试一个承诺,你必须使用 .then()
来获取它的值,你必须异步地进行比较,所以你不能直接 return 这样的值。
注意:虽然这些实现使用调用自身的函数,但这不会导致堆栈堆积,因为它们是异步调用自身的。这意味着在函数再次调用自身之前堆栈已经完全展开,因此没有堆栈堆积。 .then()
处理程序始终是这种情况,因为 Promise 规范要求 .then()
处理程序在堆栈 returned 到 "platform code" 之前不会被调用,这意味着它有在调用 .then()
处理程序之前解开所有常规 "user code"。
在 ES7 中使用 async
和 await
在 ES7 中,你可以使用 async 和 await 来 "pause" 一个循环。这可以使这种类型的迭代更易于编码。这在结构上看起来更像是一个典型的同步循环。它使用 await
来等待 promise,并且因为函数被声明为 async
,它总是 return 是一个 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
});