我重写了代码,删除了所有不相关的内容,并使用了一种我认为在这种情况下更具可读性和吸引力的风格。
function doAsynchronousStuff()
{
return new Promise((resolve, reject) =>
{
setTimeout(() => {resolve("test")}, 0)
})
.then(console.log)
.then(doAsynchronousStuff);
}
我们应该记住JS有an event loop来分析执行流程,特别是
-
setTimeout 发布要在事件循环的下一个1 周期执行的参数函数。
-
then 发布要在事件循环的下一个周期执行的参数函数。
事件循环的存在很重要,因为函数在重新进入循环之前将消息发布到它run-to-completion。
还需要对 Promise 有很好的了解,例如知道 then 返回一个新的 Promise。
当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")(或更好的包含它的闭包),将承诺设置为已解决,并安排其在下一个循环中继续
Execution stack Event loop messages
resolve console.log
resolve 结束,我们又遇到了 run-to-completion 的情况。
Execution stack Event loop messages
console.log
console.log 被执行。实际上是执行了一个调用console.log的函数,这个函数是在调用then时由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,这是编译器优化,不是代码的属性。
tail-call 是代码的一个属性,但该代码不是 tail-call,因为它一开始就不是递归的。
在编程意义上也不是迭代(而在理论意义上它是),因为迭代是通过特定的构造实现的(如for、@987654356 @, goto)。
由于迭代、递归和调度重叠,这里的边界很模糊。
最后肯定是一个碰巧引用自身的非终止过程的情况。
1我们在这里做一个简化,它不一定是非常下一个循环,它只是一个未来的循环。