Javascript引擎中的尾部调用优化实现

Ben*_*Ben 7 javascript firefox v8 chromium

截至2019年2月71.0.3578.98在Mac上的Chrome版本中,以下程序 Uncaught RangeError: Maximum call stack size exceeded error.的计数为16516

const a = x => {
  console.log(x)
  a(x + 1)
}

a(1)
Run Code Online (Sandbox Code Playgroud)

我已经做了很多Google搜寻工作,但找不到任何讨论Chrome或其他浏览器对尾部呼叫优化(TCO)支持或将来实现它的计划的文章。

我的两个问题是:

  1. Chrome或任何其他浏览器或Javascript引擎当前是否支持TCO
  2. 是否计划在不久的将来在任何JavaScript引擎中实施TCO

我发现的帖子大多是旧的(2016年或更早的版本)或令人困惑。例如https://www.chromestatus.com/feature/5516876633341952

And*_*erg 16

TCO,或更确切地说,JavaScript中的尾部消除功能(在讨论中通常也称为“正确的尾部调用(PTC)”)是一个漫长而悲伤的故事。

大约在2011年,TC39(JavaScript标准委员会)决定在即将到来的ES6标准中采用强制性TCE,并得到所有主要浏览器供应商的一致同意。

2015年,正式采用了新标准,名称为EcmaScript2015。这时,实际上没有任何浏览器实现TCE,这主要是因为ES2015中有太多新功能被认为更重要,因此无法使用。(今天,ES6尚不存在JS功能建议及其采用的过程,其中包括生产引擎中两个实现的要求。)

2016年初,Safari和Chrome都实施了TCE。Safari宣布发货,而Chrome则将其保留为“实验功能”标志。其他浏览器(Firefox和Internet Explorer / Edge)也开始对其进行调查,并产生了新的想法。讨论最终演变为这是否是一个可行的功能。Edge在为Windows ABI高效实现它方面遇到问题,Firefox担心开发人员从堆栈跟踪中调用“丢失”的体验(此问题已在2011年进行了详细讨论)。

为了在挽救尾部调用功能时解决其中的一些问题,包括Chrome和Edge团队在内的数名成员建议显式地进行尾部调用,即要求在return语句中附加一个附加关键字以选择加入尾部调用。语义。这些所谓的“ 语法尾部调用 ”(STC)是在Chrome中实现的,作为概念证明。

在2016年5月的TC39会议上,几乎一整天都在不解决的情况下广泛讨论了尾叫问题。Firefox和Edge明确表示,它们将不执行标准中指定的TCE。Firefox成员建议将其取出。Safari和Chrome对此并不认同,Safari团队明确表示他们无意取消TCE。语法尾部调用的提议也被拒绝,尤其是Safari。委员会陷入僵局。您可以阅读此讨论会议记录

从技术上讲,据我所知,这种僵局今天仍然存在。但是,实际上,对JavaScript的尾调用几乎已死,并且尚不清楚它们是否会回来。至少那是灾难性会议后Chrome小组的结论,这导致决定从Chrome删除尾部调用的实现,以简化引擎并防止钻头腐烂。它们仍然在Safari中可用。

披露:直到2017年,我还是TC39和Chrome / V8团队的成员,所以我的观点可能会带有偏见。

  • (可悲的是)非常说明性的答案。仅供将来参考,值得一提的是,有一个经过彻底测试的库 https://github.com/glathoud/fext,它可能是 Trampoline 等模式的良好替代方案 (2认同)
  • “语法尾调用”将是一个很好的补充 - 任何支持 PTC 的语言都将受益于某种语法方式来标记开发人员使用尾调用递归的意图,以帮助未来的维护人员不会意外地重构优化。 (2认同)

Joh*_*anP 6

甚至TCO似乎对我们所有人来说都是梦dream以求的,通过使用一种trampoline技术,您可以轻松地将代码转换为运行,就好像它在进行尾部优化一样。

const a = x => {
  if(x > 500000) {
      console.log(x);
      return; 
  }
  return ()=> a(x + 1); //you return a function, it hasn't been called yet
}

const trampoline = fn => (...args) => {
  let result = fn(...args)
  //repeatedly call the function till you hit your base case
  while (typeof result === 'function') {
    result = result();
  }
  
  return result;
}

var t = trampoline(a);
t(1);
Run Code Online (Sandbox Code Playgroud)

  • 这非常聪明,但它只适用于在尾部位置调用“自己”的函数,而不适用于其他函数。 (2认同)