Tail Call Optimization implementation in Javascript Engines

Even tho TCO seems to be a pipe dream for all of us, by using a trampoline technique, you can easily convert your code to run as if it is being tail optimized.

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);

TCO, or rather, Tail Call Elimination in JavaScript -- also often referred to as Proper Tail Calls (PTC) in discussions -- is a long and sad story.

Around 2011, TC39 (the JavaScript standards committee) decided to adopt mandatory TCE for the forthcoming ES6 standard, with consensus from all major browser vendors.

In 2015, the new standard was officially adopted, under the name EcmaScript 2015. At this point, no browser had actually implemented TCE, mostly because there were too many new features in ES2015 that were deemed more important to get out. (Today's process for JS feature proposals and their adoption, which includes the requirement of two implementations in production engines, did not yet exist for ES6.)

In early 2016, both Safari and Chrome implemented TCE. Safari announced shipping it, while Chrome kept it behind an Experimental Feature flag. Other browsers (Firefox and Internet Explorer / Edge) started looking into it as well and had second thoughts. Discussion evolved whether this is a viable feature after all. Edge had problems implementing it efficiently for the Windows ABI, Firefox was concerned about the developer experience of calls "missing" from stack traces (an issue that was already discussed at length in 2011).

In an attempt to address some of these concerns while rescuing the tail call feature, several members, including the Chrome and Edge teams, proposed to make tail calls explicit, i.e., require return statements to be annotated with an additional keyword to opt into tail call semantics. These so-called "syntactic tail calls" (STC) were implemented in Chrome as a proof of concept.

At the May 2016 TC39 meeting the issue of tail calls was discussed extensively for almost an entire day with no resolution. Firefox and Edge made clear that they would not implement TCE as specified in the standard. Firefox members proposed to take it out. Safari and Chrome did not agree with that, and the Safari team made clear that they have no intention of unshipping TCE. The proposal for syntactic tail calls was rejected as well, especially by Safari. The committee was in an impasse. You can read the meeting notes of this discussion.

Technically, this impasse still exists today, as far as I am aware. Practically speaking, though, tail calls for JavaScript are pretty much dead, and it's unclear whether they will ever come back. At least that was the conclusion of the Chrome team after the disastrous meeting, which led to the decision to remove the implementation of tail calls from Chrome, in order to simplify the engine and prevent bit rot. They are still available in Safari.

Disclosure: I was a member of TC39 and of the Chrome/V8 team until 2017, so my views may be biased.