我已经看过几个关于promises中递归的问题,并对如何正确实现它们感到困惑:
我把一个简单的例子放在一起(见下文) - 这只是一个例子,所以我可以理解如何使用promises工作进行递归,而不是代表我正在工作的代码。
Net-net,我想要解决的承诺,但根据节点上的输出,它无法解决。有关如何解决问题的任何见解?
var i = 0;
var countToTen = function() {
return new Promise(function(resolve, reject) {
if (i < 10) {
i++;
console.log("i is now: " + i);
return countToTen();
}
else {
resolve(i);
}
});
}
countToTen().then(console.log("i ended up at: " + i));
控制台上的输出:
> countToTen().then(console.log("i ended up at: " + i));
i is now: 1
i is now: 2
i is now: 3
i is now: 4
i is now: 5
i is now: 6
i is now: 7
i is now: 8
i is now: 9
i is now: 10
i ended up at: 10
Promise { <pending> }
承诺永远不会解决。
答案 0 :(得分:9)
如果只要i
小于10就查看代码,那么您将递归并且永远不会解决承诺。你最终解决了一个承诺。但这不是初始来电者的承诺。
您需要使用递归返回的承诺来解决。如果您使用承诺解决该系统的工作原理,它将无法解决,直到该值得到解决:
let i = 0;
const countToTen = () => new Promise((resolve, reject) => {
if (i < 10) {
i++;
console.log("i is now: " + i);
resolve(countToTen());
} else {
resolve(i);
}
});
countToTen().then(() => console.log("i ended up at: " + i));
最后一部分也有错误。你没有为then
提供一个函数,所以如果你已经做了一些实际上会等待的东西,你将首先获得"i ended up at: 0"
。
答案 1 :(得分:1)
如果你将i
作为函数的参数而不依赖于外部状态会更好
const countToTen = (i = 0) =>
new Promise ((resolve, _) =>
i < 10
? (console.log (i), resolve (countToTen (i + 1)))
: resolve (i))
countToTen () .then (console.log, console.error)
// 0 1 2 3 4 5 6 7 8 9 10
如果您将10
作为参数
const countTo = (to, from = 0) =>
new Promise ((resolve, _) =>
from < to
? (console.log (from), resolve (countTo (to, from + 1)))
: resolve (from))
countTo (7, 2) .then (console.log, console.error)
// 2 3 4 5 6 7
更通用的方法是反向折叠 - 或unfold
const unfold = (f, init) =>
f ( (x, acc) => [ x, ...unfold (f, acc) ]
, () => []
, init
)
const countTo = (to, from = 0) =>
unfold
( (next, done, acc) =>
acc <= to
? next (acc, acc + 1)
: done ()
, from
)
console.log (countTo (10))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
console.log (countTo (7, 2))
// [ 2, 3, 4, 5, 6, 7 ]
但是你想要一个异步展开,asyncUnfold
。现在,用户提供的函数f
可以是异步的,我们得到所有收集值的承诺
const asyncUnfold = async (f, init) =>
f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
, async () => []
, init
)
const delay = (x, ms = 50) =>
new Promise (r => setTimeout (r, ms, x))
const countTo = (to, from = 0) =>
asyncUnfold
( async (next, done, acc) =>
acc <= to
? next (await delay (acc), await delay (acc + 1))
: done ()
, from
)
countTo (10) .then (console.log, console.error)
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
countTo (7, 2) .then (console.log, console.error)
// [ 2, 3, 4, 5, 6, 7 ]
这是一个更实际的例子,我们有一个记录数据库,我们希望执行递归查找,或者某事......
db.getChildren
接受节点id
并仅返回节点的直接子项
traverse
接受一个节点id
,并以递归方式提取所有后代子节点(按深度优先顺序排列)
const data =
{ 0 : [ 1, 2, 3 ]
, 1 : [ 11, 12, 13 ]
, 2 : [ 21, 22, 23 ]
, 3 : [ 31, 32, 33 ]
, 11 : [ 111, 112, 113 ]
, 33 : [ 333 ]
, 333 : [ 3333 ]
}
const db =
{ getChildren : (id) =>
delay (data [id] || [])
}
const Empty =
Symbol ()
const traverse = (id) =>
asyncUnfold
( async (next, done, [ id = Empty, ...rest ]) =>
id === Empty
? done ()
: next (id, [ ...await db.getChildren (id), ...rest ])
, [ id ]
)
traverse (0) .then (console.log, console.error)
// [ 0, 1, 11, 111, 112, 113, 12, 13, 2, 21, 22, 23, 3, 31, 32, 33, 333, 3333 ]
答案 2 :(得分:0)
尽量不要在函数中使用共享可变状态(特别是当它们是异步时)。您正在使用window.i
但是任何内容都可以更改该值,因为i
值仅在您的函数中用作计数器,所以不需要这样做:
const later = (milliseconds,value) =>
new Promise(
resolve=>
setTimeout(
()=>resolve(value),
milliseconds
)
);
const countTo = toWhat => {
const recur = counter =>
later(1000,counter)
.then(
i=>{
console.log(`i is now: ${i}`);
return (i<toWhat)
? recur(i+1)
: i;
}
)
return recur(1);
}
countTo(10)
.then(
i=>console.log(`i ended up at: ${i}`)
);
答案 3 :(得分:0)
许多成员已经提到,需要解决递归返回的承诺。
我想将代码共享为async/await
语法。
const printNumber = (i) => console.log("i is now: " + i);
// recursive function to call number increment order
const recursiveCallNumber = async (i, checkCondition) => {
// if false return it, other wise continue to next step
if (!checkCondition(i)) return;
// then print
printNumber(i);
// then call again for print next number
recursiveCallNumber(++i, checkCondition);
}
await recursiveCallNumber(1, (i) => i <= 10);