// With enough hackery, stepping in and out of async functions can be made to // work as users expect. // // This test exercises the common case when we have syntactically `await // $ASYNC_FN($ARGS)` so that the calls nest as if they were synchronous // calls. It works, but there's a problem. // // onStep fires in extra places that end users would find very confusing--see // the comment marked (!) below. As a result, Debugger API consumers must do // some extra work to skip pausing there. This test is a proof of concept that // shows what sort of effort is needed. It maintains a single `asyncStack` and // skips the onStep hook if we're not running the function at top of the async // stack. Real debuggers would have to maintain multiple async stacks.
// Set up debuggee. var g = newGlobal({newCompartment: true});
g.eval(`\
async function outer() { // line 1 return (await inner()) + (await inner()) + "!"; // 2
} // 3
async function inner() { // 4 return (await leaf()) + (await leaf()); // 5
} // 6
async function leaf() { // 7 return (await Promise.resolve("m")); // 8
} // 9
`);
// Set up debugger.
let previousLine = -1;
let dbg = new Debugger(g);
let log = "";
let asyncStack = [];
// If we're entering this frame for the first time, push it to the async // stack. if (!frame.seen) {
frame.seen = true;
asyncStack.push(frame);
log += "(";
}
frame.onStep = () => { // When stepping, we sometimes pause at opcodes in older frames (!) // where all that's happening is async function administrivia. // // For example, the first time `leaf()` yields, `inner()` and // `outer()` are still on the stack; they haven't awaited yet because // control has not returned from `leaf()` to them yet. So stepping will // hop from line 8 to line 5 to line 2 as we unwind the stack, then // resume on line 8. // // Anyway: skip that noise. if (frame !== asyncStack[asyncStack.length - 1]) return;
let line = frame.script.getOffsetLocation(frame.offset).lineNumber; if (previousLine != line) {
log += line; // We stepped to a new line.
previousLine = line;
}
};
frame.onPop = completion => { // Popping the frame. But async function frames are popped multiple // times: for the "initial suspend", at each await, and on return. The // debugger offers no easy way to distinguish them (bug 1470558). // For now there's an "await" property, but bug 1470558 may come up // with a different solution, so don't rely on it! if (!completion.await) { // Returning (not awaiting or at initial suspend).
assertEq(asyncStack.pop(), frame);
log += ")";
}
};
};
// Run.
let result;
g.outer().then(v => { result = v; });
drainJobQueue();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.