javascript – 递归的界限是什么?
作者:互联网
特定
let doAsynchronousStuff = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve("abcdefg"[Math.floor(Math.random() * 7)])
}, Math.PI * 1 + Math.random())
})
.then(data => console.log(data))
.then(doAsynchronousStuff)
}
该模式是一个实现
>递归;
>尾调用优化;
>迭代;
>一个恰好引用自身的非终止程序;
要么;上面没有列出的其他常见模式?
寻找可信和/或官方来源的答案.
解决方法:
我重写了代码,消除了所有不相关的东西,并使用我认为在这种情况下更具可读性和风格的样式.
function doAsynchronousStuff()
{
return new Promise((resolve, reject) =>
{
setTimeout(() => {resolve("test")}, 0)
})
.then(console.log)
.then(doAsynchronousStuff);
}
我们应该分析执行流程,记住JS有an event loop,特别是
> setTimeout发布其参数函数,以便在事件循环的next1周期执行.
>然后发布其参数函数,以便在事件循环的下一个循环中执行.
事件循环的存在很重要,因为函数在重新进入循环之前将消息发布到它上面运行完成.
还需要很好地了解承诺,例如知道然后返回新的承诺.
执行doAsynchronousStuff时,将构造Promise对象并立即调用其参数函数.
Execution stack Event loop messages
doAsynchronousStuff
Promise constructor
Closure (resolve, reject)
这反过来调用发布事件并返回的setTimeout.
Execution stack Event loop messages
doAsynchronousStuff resolve("test")
Promise constructor
Closure (resolve, reject)
setTimeout
执行回退到doAsynchronousStuff,它设置Promise对象的延续但当然不执行它们.所以最后doAsynchronousStuff返回,我们有一个run-to-completion情况.
Execution stack Event loop messages
resolve("test")
事件循环执行resolve(“test”)(或更好的包含它的闭包),它将promise设置为已解决,并在下一个周期安排继续
Execution stack Event loop messages
resolve console.log
决心结束我们再次有一个完成运行的情况.
Execution stack Event loop messages
console.log
console.log已执行.实际上,执行调用console.log的函数,此函数在调用时由promise对象设置.
当console.log返回其promise时,会在事件循环中发布doAsynchronousStuff.
Execution stack Event loop messages
resolve doAsynchronousStuff
当resolve结束时,我们有一个run-to-completion并再次执行doAsynchronousStuff.
现在我不会在数学意义上挖掘太多,也不会在你的问题列表中的CS理论意义上挖掘太多,这样做没有实际的好处,因为我不认为这是一个理论问题.
相反,我会将自己限制在编程的角度.
到第二个doAsynchronousStuff实例被调用时,第一个实例早已消失(它运行完成).
基本上情况相当于这样做
let f = () => { console.log('hi!'); setTimeout(f, 0); }
我不会将此函数称为递归,因为递归意味着将问题破坏为较小的自动相似部分.
递归函数不必直接调用自身,也不必“使堆栈增长”,但必须根据自身定义.
如果是这样的话
let f = () => { f(); }
我称之为(严重)递归.那是什么?
我想说一个函数在编程意义上是递归的,如果你没有完成它所做的所有调用就无法完成它.
第一个例子可以在不等待f的后续调用完成的情况下完成,第二个例子则不能.
在我看来,我打电话给f的第一个版本.
关于尾部调用优化,它与此无关.
TCO transform a particular kind of recursion into a loop,它是编译器优化而不是代码的属性.
尾调用是代码的属性,但是这段代码不是尾调用,因为它首先不是递归的.
它也不是编程意义上的迭代(而在理论意义上是迭代),因为迭代是通过特定的结构实现的(比如for,while,goto).
这里的边界模糊,因为迭代,递归和调度重叠.
最后,这肯定是一个恰好引用自身的非终止程序的情况.
1我们在这里进行简化,它不是下一个周期,它只是一个未来的周期.
标签:javascript,recursion,language-agnostic,computer-science 来源: https://codeday.me/bug/20190926/1820689.html