语言:JavaScript
递归 - 不是我最喜欢的主题。
承诺 - 他们可能会感到困惑。
递归+承诺 - 我需要在带衬垫的房间进行编程。
我制作了这个小小的JS Fiddle拼图我称之为 The RecursiveFunHouse作为通过将问题简化为愚蠢而保持理智的喜剧方式。希望yall可以从我的痛苦中获得笑声:)
问题:
每次递归调用都依赖于前一次调用的结果,但为了获得结果,我必须运行异步任务并在其他子任务中使用该结果。
“Recursive Fun House”帮我把问题归结为此 - 原始的递归循环继续使用未定义的值,因为子任务仍在执行。
游乐园 - 围绕收集(-99)和99之间的随机数循环。一旦最后一个数字与新数字之间的最后一个差异为正,乐趣就结束了
打印“在这里制作1 ...在这里制作6”应该表明子任务得到了正确处理,我们有一个下一个循环的值。
目前它打印1,2,3,6,4,5 :(
recursiveFunHouse.js
var recursiveFunHouse = function(num){
console.log("Made it here 1");
var newNum = performSideTasks();
console.log("Made it here 6");
console.log("newNum");
console.log(newNum);
if(newNum-num >0 ){
recursiveFunHouse(newNum);
}
else{
console.log("The FunHouse Generated These Numbers :")
for(var i = 0; i <numList.length; i++){
console.log(numList[i]);
}
}
};
var performSideTasks = function(){
console.log("Made it here 2");
someAsyncTask().then(function(num){
anotherTask(num);
console.log("made it here 5");
return num;
});
}
var someAsyncTask = function(){
return new Promise (function(resolve, reject) {
console.log("made it here 3");
var randNum = Math.floor(Math.random()*99) + 1;
randNum *= Math.floor(Math.random()*2) == 1 ? 1 : -1;
setTimeout(function() {
numList.push(randNum)
resolve(randNum)
}, 100);
});
}
var anotherTask = function(num){
console.log("made it here 4");
console.log(num);
};
var numList= [];
recursiveFunHouse(20);
注意 - 原谅我可怜的回复陈述return num;
它只是显示了我希望能告诉我的电脑的内容。
关于递归和承诺的问题:
1)我是否应首先担心进入下一个递归循环并且承诺尚未解决?
2)如果没有,那么保持这个函数分解并强制每个递归循环等待最后一次调用的解析是什么干净的方法?
递归和承诺有时似乎是向导级别的95神奇......这就是我所说的。问-完成。
答案 0 :(得分:19)
在经典的同步递归中,递归状态存储在推送和弹出公共执行堆栈的堆栈帧中。在异步递归中,递归状态可以存储在推送的Promise对象中,并从公共的promise链中弹出。例如:
function asyncThing( asyncParam) { // something async returning a promise
return new Promise( function( resolve, reject) {
function timeOut() {
resolve( asyncParam); // resolve with parameter value passed
}
setTimeout( timeOut, 1000);
});
}
function recFun( num) { // asynchronous recursive function
// do whatever synchronous stuff when called
// ...
function decide( asyncResult) { // process async result and decide what to do
// do something with asyncResult
console.log("asyncResult: " + asyncResult);
if( asyncResult == 0)
console.log("ignition");
// decide if further recursion needed
if( asyncResult < 0)
return "lift off"; // all done
return recFun( num-1); // not all done, recurse
}
// return a promise resolved by doing something async and deciding what to do with it
// to be clear the returned promise is the one returned by .then
return asyncThing(num).then(decide);
}
// call the recursive function
recFun( 5)
.then( function(result) {console.log("done, result = " + result); })
.catch( function(err) {console.log("oops:" + err);});
运行代码以查看其效果。
此示例依赖的核心原则(魔术):
then
注册侦听器函数会返回一个待处理的promise。如果调用侦听器并从执行返回,则侦听器返回值将解析挂起的promise。如果侦听器抛出错误而不是返回,则使用抛出的值拒绝挂起的promise。当定义链的代码执行时,所有承诺链的链接和链接都是同步创建的。然后定义代码运行完成(意味着它返回事件循环而不会被异步回调中断,因为JavaScript是单线程的。)
如果和何时执行侦听器函数(因为一个promise已经完成或被拒绝),它们将在事件循环中自己调用异步执行,导致执行侦听器的代码本身就会运行完成。 / p>
这意味着在注册承诺侦听器(通过调用then
)时出现的所有日志条目出现在异步执行的侦听器函数中的任何日志条目之前,一段时间以后。承诺不涉及时间旅行。
这会让你的头疼吗?也许不是,但至少它是真的。
`
答案 1 :(得分:8)
如评论中所述,您必须稍微修改performSideTasks
以使其返回承诺:
var performSideTasks = function ()
{
console.log( "Made it here 2" )
return someAsyncTask().then( function ( num )
{
anotherTask(num);
console.log("made it here 5")
return num
} )
}
然后,您可以在主函数的then()
方法中使用异步结果。
var recursiveFunHouse = function ( num )
{
console.log( "Made it here 1" )
performSideTasks().then( function ( newNum )
{
console.log( "Made it here 6" )
console.log( "newNum" )
console.log( newNum )
if ( newNum-num > 0 )
{
recursiveFunHouse( newNum )
}
else
{
console.log( "The FunHouse Generated These Numbers :" )
for( var i = 0 ; i <numList.length ; i++ )
{
console.log( numList[i] )
}
}
} )
}