A common beginner misconception is that once you await
a promise, you can somehow go back to your mainline synchronous code. On the contrary, once you have a promise, you're stuck in asynchronous mode for any other code that depends on that result, regardless of whether the dependent code is asynchronous or not.
That's not to say you can't have runs of synchronous code along the way, only that any functions that consume a promise themselves basically become promises, transitively.
As a rule of thumb, there's 1 await
per promise, with the caveat that any callers of asynchronous code that use await
now return promises themselves.
You don't have to use async
/await
inside the method since there's only a single promise at hand. You can return the promise that page.goto
returns, passing it up to the caller and essentially stripping off a superfluous promise wrapper:
goto() {
return this.page.goto('https://playwright.dev');
}
Either way, the caller will need to await
the returned promise, unless they don't care when the navigation happens and don't need the resolved value from the promise, which is usually not the case.
To track your promises, keep your promise resolutions together in the same chain(s), occurring sequentially. Every single promise in the chain needs to be await
ed or chained with then
. As soon as a single promise is not await
ed, the chain breaks and the second part of the chain has no way of getting values from or awaiting completion of the first part.
The reason that calling code is "polluted" by promises has to do with the fact that promises are just syntactic sugar for asynchronous callbacks (think setTimeout
), and callbacks don't run in mainline code. All synchronous execution ends before any promises run. Then, when the promises resolve later on, execution resumes, running the "callback" (or code after await
or in the next .then
handler).
async
/await
just makes it appear like you're doing it all synchronously in a function, but that's just a syntactical trick, flattening out the callbacks.
If you could use await
, then resume mainline synchronous code, it would be a non-asynchronous, blocking call that ties up the single Node thread (imagine the Node version of "this page is not responding" that you may see in the browser when too much blocking CPU-bound processing occurs and the event loop doesn't get a chance to run and repaint the screen or handle an interaction).