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}`; | |
| } | |
| } |
Author
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.jsis the source filefoo-wrapped.jsis the transpiled sourcewrapper.jsis the little extra runtime that we need.The transpilation pass is trivial: it just replaces every
await expressionbyawait 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:
wrapfunction 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.WrapperErrorclass should also be eliminated because the original exception should be preserved (catch handlers often containinstanceoftests). But then we'd need a mechanism to collect the file/line pairs into an Error instance. The baseErrorclass would also take care of injecting the collected file/line pairs when it generates its stack (and theasyncStackhack 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
promiseis 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.