Consider this Javascript, executed in chrome in 2775.01ms on my MBP (please close devtools first before you test it). Why? Because under the hood ES7 use promise to implement await/async, which introduce massive overhead. Call an async function is doggy slow even if it actually doesn't do any async thing.
function sleep(t) {
return new Promise((resolve) => {
setTimeout(resolve, t);
});
}
async function small(i, n) {
if (i === Math.floor(n / 2)) {
await sleep(1000);
}
}
async function test(n) {
let t = performance.now();
for (let i = 0; i < n; i++) {
await small(i, n);
}
console.log(performance.now() - t);
}
test(5000000);
Now, I'll present you a novel approach to implement async/await. It combines CPS, state machine and exception to reconstruct call stack and avoid closure creation.
The javascript above then can transform to the javascript below. You can test it in your chrome. I'll explain it next time. These code is just prototype for proof. You don't need handwrite your async logic like these. There will be some compilers do it. Thank you!
function GetContinuation(callback, outerState) {
this.callback = callback;
this.stack = [];
this.outerState = outerState;
}
function makeContinuation(stack, index) {
if (index === stack.length) {
return function () {};
}
let func = stack[index];
let args = stack[index + 1];
let parent = makeContinuation(stack, index + 2);
return function () {
return func.apply(parent, args);;
};
}
function bind(func, this_, args) {
return function () {
return func.apply(this_, args);
};
}
function callcc(callback, outerState) {
throw new GetContinuation(callback, outerState);
}
function sleep(timeout, outerState) {
return callcc(function (continuation) {
setTimeout(continuation, timeout);
}, outerState);
}
function small(i, n, outerState) {
try {
if (i === Math.floor(n / 2)) {
sleep(1000, 0);
}
} catch (exn) {
if (exn instanceof GetContinuation) {
exn.stack.push(small_async, [exn.outerState, i, n]);
exn.outerState = outerState;
}
throw exn;
}
}
function small_async(state, i, n) {
try {
while (true) {
switch (state) {
case 0:
return this();
}
}
} catch (exn) {
if (exn instanceof GetContinuation) {
return exn.callback(bind(small_async, this, [exn.outerState, i, n]));
}
throw exn;
}
}
function test(n, outerState) {
var i;
var t;
try {
t = performance.now();
i = 0;
while (i < n) {
small(i, n, 0);
i ++;
}
console.log(performance.now() - t);
} catch (exn) {
if (exn instanceof GetContinuation) {
exn.stack.push(test_async, [exn.outerState, t, i, n]);
exn.outerState = outerState;
}
throw exn;
}
}
function test_async(state, t, i, n) {
try {
while (true) {
switch (state) {
case 0:
i++;
case 1:
if (i >= n) {
state = 2;
continue;
}
small(i, n, 0);
continue;
case 2:
console.log(performance.now() - t);
return this();
}
}
} catch (exn) {
if (exn instanceof GetContinuation) {
return exn.callback(bind(test_async, this, [exn.outerState, t, i, n]));
}
throw exn;
}
}
function main() {
test(5000000, 0);
}
function exec(main) {
var task;
task = main;
while (true) {
try {
return task();
} catch (exn) {
if (exn instanceof GetContinuation) {
task = function () {
return exn.callback(makeContinuation(exn.stack, 0));
};
} else {
throw exn;
}
}
}
}
exec(main);
|