编程语言
首页 > 编程语言> > javascript – 递归的界限是什么?

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