- Published on
Cross browser generator functions
- Authors
- Name
- Guillaume C. Marty
- Tech Leader & Coach, available for hire
Power skill coach at Skiller Whale
A quick word on generators
This post will not discuss generators as there are valuable resources all over the web. Just remember that generators can be used to create a set of iterable items (possibly a virtual or infinite set like sequences in mathematics) or to do step by step execution of a program.
The current state of generators implementation
Chrome implements the latest ES6 draft. So if you follow the spec, you should not have any troubles.
For Firefox, things are a bit more complicated because from version 2 to 25, an old version of the generators is implemented. Version 26 has generators on par with Chrome, so this post will be outdated in a very near future, but in the meantime, I wanted a way to use generators so that they work on the current version of both browsers.
What Firefox needs
To use the generators, Firefox requires to switch to JavaScript version 1.7 mode:
<script type="application/javascript;version=1.7">
/* Code... */
</script>
Otherwise, the keyword yield
is not recognized. Also, the syntax implemented doesn’t accept the function* myGenerator
as defined by the spec.
Of course, Chrome doesn’t recognise script tags formatted like this and doesn’t execute their content.
A workaround
The solution I came with is not very clean, but it seems to work.
First, you need to develop your code using the latest draft, the one implemented in Chrome. Then wrap this code in a inactive tag. I used a custom type
attribute to prevent its execution.
Then, this code is appended to the DOM in a newly created script
element. In Firefox, we set the correct type
attribute and transform the function*
notation:
<!doctype html>
<html>
<head>
<title>Cross-browser generator functions</title>
</head>
<body onload="executeGeneratorCode(document.getElementById('js-code').text)">
<script id="js-code" type="text/javascript-inert">
// Your code using a generator goes here:
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
function* fibonacci() {
var fn1 = 1;
var fn2 = 1;
while (1) {
var current = fn2;
fn2 = fn1;
fn1 = fn1 + current;
yield current;
}
}
var sequence = fibonacci();
console.log(sequence.next()); // 1
console.log(sequence.next()); // 1
console.log(sequence.next()); // 2
console.log(sequence.next()); // 3
console.log(sequence.next()); // 5
console.log(sequence.next()); // 8
console.log(sequence.next()); // 13
</script>
<script>
var OLD_SYNTAX = false // Default value.
var currentScript = document.currentScript
function executeGeneratorCode(code) {
var scriptElement = document.createElement('script')
if (OLD_SYNTAX) {
code = code.replace(/\bfunction *\* +/g, 'function ')
scriptElement.setAttribute(
'type',
'application/javascript;version=1.7'
)
}
scriptElement.text = code
currentScript.parentNode.insertBefore(scriptElement, currentScript)
}
</script>
<script type="application/javascript;version=1.7">
// Here, we test the old syntax in Firefox only.
var OLD_SYNTAX = function() {
try {
eval('(function() { yield 5; }())');
return true;
} catch (e) {
return false;
}
}();
</script>
</body>
</html>
Conclusion
This seems to work but I didn’t do extensive testing and there might be other differences between both implementations. Also, using a regexp to remove the function*
is not the best way, but at this point, Esprima doesn’t support ES6, so there are no standalone parsers available.
Hopefully this code will continue to work when Firefox supports the new syntax.
I reckon this method is a bit complicated, so if you know better/cleaner way to achieve this, let me know via a comment!
This article is part of a series about ES6 generators, together with: