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

时间:2019-02-16 03:12:40

标签: javascript firefox chromium v8

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

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

a(1)

我已经做了很多谷歌搜索,但是找不到有关Chrome或其他浏览器对尾部呼叫优化(TCO)支持的任何文章,也没有找到实现它的任何未来计划。

我的两个问题是:

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

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

2 个答案:

答案 0 :(得分:4)

TCO,或者说JavaScript中的尾叫 Elimination (在讨论中通常也称为“正确尾叫(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团队在内的数名成员建议进行电话尾部 explicit ,即要求return语句为带有附加关键字的注释,以加入尾调用语义。这些所谓的“ syntactic tail calls”(STC)是在Chrome中实现的,作为概念证明。

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

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

披露:直到2017年,我还是TC39和Chrome / V8团队的一员,所以我的看法可能有偏见。

答案 1 :(得分:1)

即使对于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);