Ben Newman
(Meteor)
Hammer Lab @
Icahn
September 23, 2014
{ github, twitter, instagram, facebook }.com/benjamn
Why isn't every Python project using Python 3 yet?
Crazy idea: ease into the new language by simulating its most useful features in the current version of JavaScript (ECMAScript 5).
=>
function syntax[3, 1, 10, 28].sort((a, b) => a - b)
var recast = require("recast"); var n = recast.types.namedTypes; var b = recast.types.builders; var ast = recast.parse( "[3, 1, 10, 28].sort((a, b) => a - b)" );
recast.visit(ast, { });
recast.visit(ast, { visitArrowFunctionExpression: function(path) { } });
recast.visit(ast, { visitArrowFunctionExpression: function(path) { var node = path.node; } });
recast.visit(ast, { visitArrowFunctionExpression: function(path) { var node = path.node; if (!n.BlockStatement.check(node.body)) { } } });
recast.visit(ast, { visitArrowFunctionExpression: function(path) { var node = path.node; if (!n.BlockStatement.check(node.body)) { n.Expression.assert(node.body); } } });
recast.visit(ast, { visitArrowFunctionExpression: function(path) { var node = path.node; if (!n.BlockStatement.check(node.body)) { n.Expression.assert(node.body); node.body = b.blockStatement([b.returnStatement(node.body)]); } } });
recast.visit(ast, { visitArrowFunctionExpression: function(path) { var node = path.node; if (!n.BlockStatement.check(node.body)) { n.Expression.assert(node.body); node.body = b.blockStatement([b.returnStatement(node.body)]); node.expression = false; } } });
recast.visit(ast, { visitArrowFunctionExpression: function(path) { var node = path.node; if (!n.BlockStatement.check(node.body)) { n.Expression.assert(node.body); node.body = b.blockStatement([b.returnStatement(node.body)]); node.expression = false; } var funExp = b.functionExpression( node.id, node.params, node.body, node.generator, node.expression ); } });
recast.visit(ast, { visitArrowFunctionExpression: function(path) { var node = path.node; if (!n.BlockStatement.check(node.body)) { n.Expression.assert(node.body); node.body = b.blockStatement([b.returnStatement(node.body)]); node.expression = false; } var funExp = b.functionExpression( node.id, node.params, node.body, node.generator, node.expression ); return b.callExpression( b.memberExpression(funExp, b.identifier("bind"), false), [b.thisExpression()] ); } });
console.log(recast.print(ast).code); // Which prints: [3, 1, 10, 28].sort(function(a, b) { return a - b; }.bind(this))
If you already have a build step for static resources, you can be cooking with arrow functions in a matter of minutes!
var recast = require("recast"); var ast = recast.parse(source); transform(ast); // Anything goes. console.log(recast.print(ast).code);
find ~/www/html/js/lib | \ grep "\.js$" | \ time parallel ~/www/scripts/bin/classify --update 228.03s user 12.25s system 1229% cpu 19.548 total
function *permutations(list) { if (list.length < 2) { yield list; } else { var first = list.slice(0, 1); for (var p of permutations(list.slice(1))) { for (var i = 0; i < list.length; ++i) { yield Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); } } } }
var g = permutations([1, 3, 2]); g.next().value; // [1, 3, 2] g.next().value; // [3, 1, 2] g.next().value; // [3, 2, 1] g.next().value; // [1, 2, 3] g.next().value; // [2, 1, 3] g.next().value; // [2, 3, 1] g.next().done; // true
function*
or yield
?
function *permutations(list) { if (list.length < 2) { yield list; } else { var first = list.slice(0, 1); for (var p of permutations(list.slice(1))) { for (var i = 0; i < list.length; ++i) { yield Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); } } } }
var g = permutations([1, 3, 2]); var count = 0; for (var p of g) { console.log(p); ++count; } console.log(count); // 6
function*
or yield
?
I've been jokingly referring to this side project as "my life's
work" for so long that I'm terrified it might
actually be finished soon.
— Ben Newman (@benjamn)
May 8, 2013
function *permutations(list) { if (list.length < 2) { yield list; } else { var first = list.slice(0, 1); var ps = permutations(list.slice(1)); for (var p of ps) { for (var i = 0; i < list.length; ++i) { yield Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); } } } }
function *permutations(list) { var first, ps, p, i; if (list.length < 2) { yield list; } else { first = list.slice(0, 1); ps = permutations(list.slice(1)); for (p of ps) { for (i = 0; i < list.length; ++i) { yield Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); } } } }
function *permutations(list) { var first, ps, p, t$0, t$1, i; if (list.length < 2) { yield list; } else { first = list.slice(0, 1); ps = permutations(list.slice(1)); for (p of ps) { for (i = 0; i < list.length; ++i) { yield Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); } } } }
function *permutations(list) { var first, ps, p, t$0, t$1, i; if (list.length < 2) { yield list; } else { first = list.slice(0, 1); ps = permutations(list.slice(1)); t$0 = regeneratorRuntime.values(ps); while (!(t$1 = t$0.next()).done) { p = t$1.value; for (i = 0; i < list.length; ++i) { yield Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); } } } }
function permutations(list) { var first, ps, p, t$0, t$1, i; return (function*() { if (list.length < 2) { yield list; } else { first = list.slice(0, 1); ps = permutations(list.slice(1)); t$0 = regeneratorRuntime.values(ps); while (!(t$1 = t$0.next()).done) { p = t$1.value; for (i = 0; i < list.length; ++i) { yield Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); } } } })(); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) {
}); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) {
} }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; }
} }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list;
} }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list; case 3: context.next = 19; break;
} }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list; case 3: context.next = 19; break;
case 19: case "end": return context.stop(); } }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list; case 3: context.next = 19; break; case 5: first = list.slice(0, 1); ps = permutations(list.slice(1)); t$0 = regeneratorRuntime.values(ps);
case 19: case "end": return context.stop(); } }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list; case 3: context.next = 19; break; case 5: first = list.slice(0, 1); ps = permutations(list.slice(1)); t$0 = regeneratorRuntime.values(ps); case 8: if ((t$1 = t$0.next()).done) { context.next = 19; break; } p = t$1.value;
case 19: case "end": return context.stop(); } }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list; case 3: context.next = 19; break; case 5: first = list.slice(0, 1); ps = permutations(list.slice(1)); t$0 = regeneratorRuntime.values(ps); case 8: if ((t$1 = t$0.next()).done) { context.next = 19; break; } p = t$1.value;
i = 0; case 11: if (!(i < list.length)) { context.next = 17; break; } case 19: case "end": return context.stop(); } }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list; case 3: context.next = 19; break; case 5: first = list.slice(0, 1); ps = permutations(list.slice(1)); t$0 = regeneratorRuntime.values(ps); case 8: if ((t$1 = t$0.next()).done) { context.next = 19; break; } p = t$1.value;
i = 0; case 11: if (!(i < list.length)) { context.next = 17; break; } context.next = 14; return Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); case 19: case "end": return context.stop(); } }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list; case 3: context.next = 19; break; case 5: first = list.slice(0, 1); ps = permutations(list.slice(1)); t$0 = regeneratorRuntime.values(ps); case 8: if ((t$1 = t$0.next()).done) { context.next = 19; break; } p = t$1.value;
i = 0; case 11: if (!(i < list.length)) { context.next = 17; break; } context.next = 14; return Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); case 14: ++i; context.next = 11; break; case 19: case "end": return context.stop(); } }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list; case 3: context.next = 19; break; case 5: first = list.slice(0, 1); ps = permutations(list.slice(1)); t$0 = regeneratorRuntime.values(ps); case 8: if ((t$1 = t$0.next()).done) { context.next = 19; break; } p = t$1.value;
i = 0; case 11: if (!(i < list.length)) { context.next = 17; break; } context.next = 14; return Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); case 14: ++i; context.next = 11; break; case 17: context.next = 8; break; case 19: case "end": return context.stop(); } }); }
function permutations(list) { var first, ps, p, t$0, t$1, i; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.next) { case 0: if (!(list.length < 2)) { context.next = 5; break; } context.next = 3; return list; case 3: context.next = 19; break; case 5: first = list.slice(0, 1); ps = permutations(list.slice(1)); t$0 = regeneratorRuntime.values(ps); case 8: if ((t$1 = t$0.next()).done) { context.next = 19; break; } p = t$1.value;
i = 0; case 11: if (!(i < list.length)) { context.next = 17; break; } context.next = 14; return Array.prototype.concat.call( p.slice(0, i), // prefix first, // middle p.slice(i) // suffix ); case 14: ++i; context.next = 11; break; case 17: context.next = 8; break; case 19: case "end": return context.stop(); } }, permutations, this); }
ForStatement
case "ForStatement":
case "ForStatement": var head = loc(); var update = loc(); var after = loc();
case "ForStatement": var head = loc(); var update = loc(); var after = loc(); if (stmt.init) { self.explode(path.get("init"), true); }
case "ForStatement": var head = loc(); var update = loc(); var after = loc(); if (stmt.init) { self.explode(path.get("init"), true); } self.mark(head);
case "ForStatement": var head = loc(); var update = loc(); var after = loc(); if (stmt.init) { self.explode(path.get("init"), true); } self.mark(head); if (stmt.test) { self.jumpIfNot(self.explodeExpression(path.get("test")), after); }
case "ForStatement": var head = loc(); var update = loc(); var after = loc(); if (stmt.init) { self.explode(path.get("init"), true); } self.mark(head); if (stmt.test) { self.jumpIfNot(self.explodeExpression(path.get("test")), after); } self.leapManager.withEntry( new leap.LoopEntry(after, update, labelId), function() { self.explodeStatement(path.get("body")); } );
case "ForStatement": var head = loc(); var update = loc(); var after = loc(); if (stmt.init) { self.explode(path.get("init"), true); } self.mark(head); if (stmt.test) { self.jumpIfNot(self.explodeExpression(path.get("test")), after); } self.leapManager.withEntry( new leap.LoopEntry(after, update, labelId), function() { self.explodeStatement(path.get("body")); } ); self.mark(update);
case "ForStatement": var head = loc(); var update = loc(); var after = loc(); if (stmt.init) { self.explode(path.get("init"), true); } self.mark(head); if (stmt.test) { self.jumpIfNot(self.explodeExpression(path.get("test")), after); } self.leapManager.withEntry( new leap.LoopEntry(after, update, labelId), function() { self.explodeStatement(path.get("body")); } ); self.mark(update); if (stmt.update) { self.explode(path.get("update"), true); }
case "ForStatement": var head = loc(); var update = loc(); var after = loc(); if (stmt.init) { self.explode(path.get("init"), true); } self.mark(head); if (stmt.test) { self.jumpIfNot(self.explodeExpression(path.get("test")), after); } self.leapManager.withEntry( new leap.LoopEntry(after, update, labelId), function() { self.explodeStatement(path.get("body")); } ); self.mark(update); if (stmt.update) { self.explode(path.get("update"), true); } self.jump(head);
case "ForStatement": var head = loc(); var update = loc(); var after = loc(); if (stmt.init) { self.explode(path.get("init"), true); } self.mark(head); if (stmt.test) { self.jumpIfNot(self.explodeExpression(path.get("test")), after); } self.leapManager.withEntry( new leap.LoopEntry(after, update, labelId), function() { self.explodeStatement(path.get("body")); } ); self.mark(update); if (stmt.update) { self.explode(path.get("update"), true); } self.jump(head); self.mark(after);
case "ForStatement": var head = loc(); var update = loc(); var after = loc(); if (stmt.init) { self.explode(path.get("init"), true); } self.mark(head); if (stmt.test) { self.jumpIfNot(self.explodeExpression(path.get("test")), after); } self.leapManager.withEntry( new leap.LoopEntry(after, update, labelId), function() { self.explodeStatement(path.get("body")); } ); self.mark(update); if (stmt.update) { self.explode(path.get("update"), true); } self.jump(head); self.mark(after); break;
loc()
and this.mark(loc)
loc()
and this.mark(loc)
// Offsets into this.listing that could be used as targets for branches or // jumps are represented as numeric Literal nodes. function loc() { return { type: "Literal", value: -1 }; }
loc()
and this.mark(loc)
// Offsets into this.listing that could be used as targets for branches or // jumps are represented as numeric Literal nodes. function loc() { return require("recast").types.builders.literal(-1); }
loc()
and this.mark(loc)
// Offsets into this.listing that could be used as targets for branches or // jumps are represented as numeric Literal nodes. function loc() { return b.literal(-1); }
loc()
and this.mark(loc)
// Offsets into this.listing that could be used as targets for branches or // jumps are represented as numeric Literal nodes. function loc() { return b.literal(-1); } // Sets the exact value of the given location to the offset of the next // Statement emitted. Emitter.prototype.mark = function(loc) { assert.strictEqual(loc.type, "Literal"); var index = this.listing.length; if (loc.value === -1) { loc.value = index; } else { // Locations can be marked redundantly, but their values cannot change // once set the first time. assert.strictEqual(loc.value, index); } this.marked[index] = true; return loc; };
loc()
and this.mark(loc)
// Offsets into this.listing that could be used as targets for branches or // jumps are represented as numeric Literal nodes. function loc() { return b.literal(-1); } // Sets the exact value of the given location to the offset of the next // Statement emitted. Emitter.prototype.mark = function(loc) { require("recast").types.namedTypes.Literal.assert(loc); var index = this.listing.length; if (loc.value === -1) { loc.value = index; } else { // Locations can be marked redundantly, but their values cannot change // once set the first time. assert.strictEqual(loc.value, index); } this.marked[index] = true; return loc; };
loc()
and this.mark(loc)
// Offsets into this.listing that could be used as targets for branches or // jumps are represented as numeric Literal nodes. function loc() { return b.literal(-1); } // Sets the exact value of the given location to the offset of the next // Statement emitted. Emitter.prototype.mark = function(loc) { n.Literal.assert(loc); var index = this.listing.length; if (loc.value === -1) { loc.value = index; } else { // Locations can be marked redundantly, but their values cannot change // once set the first time. assert.strictEqual(loc.value, index); } this.marked[index] = true; return loc; };
this.jumpIfNot(expr, loc)
this.jumpIfNot(expr, loc)
// Conditional jump, with the condition negated. Emitter.prototype.jumpIfNot = function(test, toLoc) { };
this.jumpIfNot(expr, loc)
// Conditional jump, with the condition negated. Emitter.prototype.jumpIfNot = function(test, toLoc) { n.Expression.assert(test); n.Literal.assert(toLoc); };
this.jumpIfNot(expr, loc)
// Conditional jump, with the condition negated. Emitter.prototype.jumpIfNot = function(test, toLoc) { n.Expression.assert(test); n.Literal.assert(toLoc); var negatedTest; if (n.UnaryExpression.check(test) && test.operator === "!") { } };
this.jumpIfNot(expr, loc)
// Conditional jump, with the condition negated. Emitter.prototype.jumpIfNot = function(test, toLoc) { n.Expression.assert(test); n.Literal.assert(toLoc); var negatedTest; if (n.UnaryExpression.check(test) && test.operator === "!") { // Avoid double negation. negatedTest = test.argument; } };
this.jumpIfNot(expr, loc)
// Conditional jump, with the condition negated. Emitter.prototype.jumpIfNot = function(test, toLoc) { n.Expression.assert(test); n.Literal.assert(toLoc); var negatedTest; if (n.UnaryExpression.check(test) && test.operator === "!") { // Avoid double negation. negatedTest = test.argument; } else { negatedTest = b.unaryExpression("!", test); } };
this.jumpIfNot(expr, loc)
// Conditional jump, with the condition negated. Emitter.prototype.jumpIfNot = function(test, toLoc) { n.Expression.assert(test); n.Literal.assert(toLoc); var negatedTest; if (n.UnaryExpression.check(test) && test.operator === "!") { // Avoid double negation. negatedTest = test.argument; } else { negatedTest = b.unaryExpression("!", test); } this.emit(b.ifStatement( negatedTest, b.blockStatement([ this.assign(this.contextProperty("next"), toLoc), b.breakStatement() ]) )); };
this.explodeStatement(path)
this.explodeStatement(path)
Emitter.prototype.explodeStatement = function(path) { };
this.explodeStatement(path)
Emitter.prototype.explodeStatement = function(path) { var stmt = path.value; n.Statement.assert(stmt); };
this.explodeStatement(path)
Emitter.prototype.explodeStatement = function(path) { var stmt = path.value; n.Statement.assert(stmt); if (n.BlockStatement.check(stmt)) { return path.get("body").each(self.explodeStatement, self); } };
this.explodeStatement(path)
Emitter.prototype.explodeStatement = function(path) { var stmt = path.value; n.Statement.assert(stmt); if (n.BlockStatement.check(stmt)) { return path.get("body").each(self.explodeStatement, self); } if (!require("./meta").containsLeap(stmt)) { self.emit(stmt); return; } };
this.explodeStatement(path)
Emitter.prototype.explodeStatement = function(path) { var stmt = path.value; n.Statement.assert(stmt); if (n.BlockStatement.check(stmt)) { return path.get("body").each(self.explodeStatement, self); } if (!require("./meta").containsLeap(stmt)) { self.emit(stmt); return; } switch (stmt.type) { } };
this.explodeStatement(path)
Emitter.prototype.explodeStatement = function(path) { var stmt = path.value; n.Statement.assert(stmt); if (n.BlockStatement.check(stmt)) { return path.get("body").each(self.explodeStatement, self); } if (!require("./meta").containsLeap(stmt)) { self.emit(stmt); return; } switch (stmt.type) { case "ForStatement": } };
this.explodeStatement(path)
Emitter.prototype.explodeStatement = function(path) { var stmt = path.value; n.Statement.assert(stmt); if (n.BlockStatement.check(stmt)) { return path.get("body").each(self.explodeStatement, self); } if (!require("./meta").containsLeap(stmt)) { self.emit(stmt); return; } switch (stmt.type) { case "ForStatement": // See previous slides. break; } };
this.explodeStatement(path)
Emitter.prototype.explodeStatement = function(path) { var stmt = path.value; n.Statement.assert(stmt); if (n.BlockStatement.check(stmt)) { return path.get("body").each(self.explodeStatement, self); } if (!require("./meta").containsLeap(stmt)) { self.emit(stmt); return; } switch (stmt.type) { case "ForStatement": // See previous slides. break; ... } };
this.explodeStatement(path)
Emitter.prototype.explodeStatement = function(path) { var stmt = path.value; n.Statement.assert(stmt); if (n.BlockStatement.check(stmt)) { return path.get("body").each(self.explodeStatement, self); } if (!require("./meta").containsLeap(stmt)) { self.emit(stmt); return; } switch (stmt.type) { case "ForStatement": // See previous slides. break; ... default: throw new Error("unknown Statement of type " + JSON.stringify(stmt.type)); } };
function *attempt(block) { console.log("before"); try { var result = block(); } catch (thrown) { yield thrown; } console.log("after"); return result; }
function attempt(block) { var result; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.prev = context.next) { } }, attempt, this); }
function attempt(block) { var result; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.prev = context.next) { case 0: console.log("before"); context.prev = 1; result = block(); context.next = 9; break; } }, attempt, this); }
function attempt(block) { var result; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.prev = context.next) { case 0: console.log("before"); context.prev = 1; result = block(); context.next = 9; break; case 5: context.prev = 5; context.t0 = context.catch(1); context.next = 9; return context.t0; } }, attempt, this); }
function attempt(block) { var result; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.prev = context.next) { case 0: console.log("before"); context.prev = 1; result = block(); context.next = 9; break; case 5: context.prev = 5; context.t0 = context.catch(1); context.next = 9; return context.t0; case 9: console.log("after"); return context.abrupt("return", result); } }, attempt, this); }
function attempt(block) { var result; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.prev = context.next) { case 0: console.log("before"); context.prev = 1; result = block(); context.next = 9; break; case 5: context.prev = 5; context.t0 = context.catch(1); context.next = 9; return context.t0; case 9: console.log("after"); return context.abrupt("return", result); case 11: case "end": return context.stop(); } }, attempt, this); }
function attempt(block) { var result; return regeneratorRuntime.wrap(function(context) { while (1) switch (context.prev = context.next) { case 0: console.log("before"); context.prev = 1; result = block(); context.next = 9; break; case 5: context.prev = 5; context.t0 = context.catch(1); context.next = 9; return context.t0; case 9: console.log("after"); return context.abrupt("return", result); case 11: case "end": return context.stop(); } }, attempt, this, [[1, 5]]); }
while (true) { }
while (true) { if (state === GenStateSuspendedYield) { context.sent = arg; } else { delete context.sent; } }
while (true) { if (state === GenStateSuspendedYield) { context.sent = arg; } else { delete context.sent; } state = GenStateExecuting; }
while (true) { if (state === GenStateSuspendedYield) { context.sent = arg; } else { delete context.sent; } state = GenStateExecuting; try { } catch (thrown) { } }
while (true) { if (state === GenStateSuspendedYield) { context.sent = arg; } else { delete context.sent; } state = GenStateExecuting; try { var value = innerFn.call(self, context); } catch (thrown) { } }
while (true) { if (state === GenStateSuspendedYield) { context.sent = arg; } else { delete context.sent; } state = GenStateExecuting; try { var value = innerFn.call(self, context); // If an exception is thrown from innerFn, we leave state === // GenStateExecuting and loop back for another invocation. state = context.done ? GenStateCompleted : GenStateSuspendedYield; } catch (thrown) { } }
while (true) { if (state === GenStateSuspendedYield) { context.sent = arg; } else { delete context.sent; } state = GenStateExecuting; try { var value = innerFn.call(self, context); // If an exception is thrown from innerFn, we leave state === // GenStateExecuting and loop back for another invocation. state = context.done ? GenStateCompleted : GenStateSuspendedYield; return { value: value, done: context.done }; } catch (thrown) { } }
while (true) { if (state === GenStateSuspendedYield) { context.sent = arg; } else { delete context.sent; } state = GenStateExecuting; try { var value = innerFn.call(self, context); // If an exception is thrown from innerFn, we leave state === // GenStateExecuting and loop back for another invocation. state = context.done ? GenStateCompleted : GenStateSuspendedYield; return { value: value, done: context.done }; } catch (thrown) { state = GenStateCompleted; context.dispatchException(thrown); } }
That's pretty much as hard as transpilation gets.
async
functionsasync function chainAnimations(elem, animations) { var ret = null; for (var i = 0; i < animations.length; ++i) ret = await animations[i](elem); return ret; }
chainAnimations($("#card"), [ flip(100), slide(200), ... ]).then(function() { console.log("all done!"); });
function chainAnimations(elem, animations) { var ret, i; return regeneratorRuntime.async(function(context) { while (1) switch (context.next) { case 0: ret = null; i = 0; case 2: if (!(i < animations.length)) { context.next = 9; break; } context.next = 5; return animations[i](elem); case 5: ret = context.sent; case 6: ++i; context.next = 2; break; case 9: return context.abrupt("return", ret); case 10: case "end": return context.stop(); } }, null, this); }
regeneratorRuntime.async = function(innerFn, self, tryList) { };
regeneratorRuntime.async = function(innerFn, self, tryList) { return new Promise(function(resolve, reject) { }); };
regeneratorRuntime.async = function(innerFn, self, tryList) { return new Promise(function(resolve, reject) { var generator = regeneratorRuntime.wrap(innerFn, self, tryList); }); };
regeneratorRuntime.async = function(innerFn, self, tryList) { return new Promise(function(resolve, reject) { var generator = regeneratorRuntime.wrap(innerFn, self, tryList); function step(method, arg) { } }); };
regeneratorRuntime.async = function(innerFn, self, tryList) { return new Promise(function(resolve, reject) { var generator = regeneratorRuntime.wrap(innerFn, self, tryList); var callNext = step.bind(null, generator.next); var callThrow = step.bind(null, generator.throw); function step(method, arg) { } }); };
regeneratorRuntime.async = function(innerFn, self, tryList) { return new Promise(function(resolve, reject) { var generator = regeneratorRuntime.wrap(innerFn, self, tryList); var callNext = step.bind(null, generator.next); var callThrow = step.bind(null, generator.throw); function step(method, arg) { try { var info = method(arg); var value = info.value; } catch (error) { reject(error); return; } } }); };
regeneratorRuntime.async = function(innerFn, self, tryList) { return new Promise(function(resolve, reject) { var generator = regeneratorRuntime.wrap(innerFn, self, tryList); var callNext = step.bind(null, generator.next); var callThrow = step.bind(null, generator.throw); function step(method, arg) { try { var info = method(arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } } }); };
regeneratorRuntime.async = function(innerFn, self, tryList) { return new Promise(function(resolve, reject) { var generator = regeneratorRuntime.wrap(innerFn, self, tryList); var callNext = step.bind(null, generator.next); var callThrow = step.bind(null, generator.throw); function step(method, arg) { try { var info = method(arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(callNext, callThrow); } } }); };
regeneratorRuntime.async = function(innerFn, self, tryList) { return new Promise(function(resolve, reject) { var generator = regeneratorRuntime.wrap(innerFn, self, tryList); var callNext = step.bind(null, generator.next); var callThrow = step.bind(null, generator.throw); function step(method, arg) { try { var info = method(arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(callNext, callThrow); } } callNext(); }); };
async
functions and the await
keyword?
Incremental transpilation is the key to avoiding the Python 3 trap.
It's how we bring about the future without choking on it.
It's how we know, sooner rather than later, how great the future will be.
It is, quite literally, linguistic time travel.
Not everything can be transpiled.
AST transforms don't always play well together.
Niceties like source maps become an absolute necessity when debugging generated code.
The language specification can change out from under you.
It's all going to be open-source.
We're only just getting started.
You are more than welcome to help.
github.com/{ benjamn/jsconf-2014, facebook/jstransform, benjamn/recast, benjamn/ast-types, facebook/regenerator, square/esnext } code.facebook.com/projects What about Traceur?