Last active
May 16, 2018 19:55
-
-
Save bjouhier/9882719d30fb171cdabdfdf55c05ac4f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const wrap = require('./wrapper').wrap; | |
function sleep(ms) { | |
return new Promise((resolve, reject) => { | |
setTimeout(resolve, ms); | |
}) | |
} | |
async function f1() { | |
await wrap(sleep(1), __filename, 9); | |
throw new Error('error1'); | |
} | |
async function f2() { | |
await wrap(f1(), __filename, 14); | |
} | |
async function f3() { | |
await wrap(f2(), __filename, 18); | |
} | |
f3().catch(err => console.error(err.asyncStack || err.stack)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function sleep(ms) { | |
return new Promise((resolve, reject) => { | |
setTimeout(resolve, ms); | |
}) | |
} | |
async function f1() { | |
await sleep(1); | |
throw new Error('error1'); | |
} | |
async function f2() { | |
await f1(); | |
} | |
async function f3() { | |
await f2(); | |
} | |
f3().catch(err => console.error(err.stack)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exports.wrap = function(promise, file, line) { | |
return promise.catch(err => { throw new WrappedError(err, file, line); }); | |
} | |
class WrappedError extends Error { | |
constructor(inner, file, line) { | |
super(inner.message); | |
this.inner = inner; | |
this.file = file; | |
this.line = line; | |
} | |
get asyncStack() { | |
return `${this.inner.asyncStack || this.inner.stack} | |
at ${this.file}:${this.line}`; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Quick hack to demo how full async stacktrace could be added to async/await, with the help of a very simple transpilation pass:
foo.js
is the source filefoo-wrapped.js
is the transpiled sourcewrapper.js
is the little extra runtime that we need.The transpilation pass is trivial: it just replaces every
await expression
byawait wrap(expression)
.In real life, this should not be achieved through transpilation but directly baked into the JS compiler instead. This would allow to optimize and fix some glitches:
wrap
function would be inlined. It would not allocate a new promise. instead it would just store the file name and line number into the promise and the core Promise class would transfer this info to the error (see below) before invoking the catch handler.WrapperError
class should also be eliminated because the original exception should be preserved (catch handlers often containinstanceof
tests). But then we'd need a mechanism to collect the file/line pairs into an Error instance. The baseError
class would also take care of injecting the collected file/line pairs when it generates its stack (and theasyncStack
hack would go away).So don't take this gist too literally. It is just some kind of pseudo code to get started. I've also omitted lots of details (like checking that
promise
is actually aPromise
).Note that, if handled by the compiler, the overhead is just the storage of the file/line pair into the promise when no exception is thrown.
There is a bit more overhead when an exception is thrown, but nothing dramatic.