skip to Main Content

Im pretty new to using the await keyword, I am used to the older "Promise" like commands used in Cypress.

However one confusion I have is when a function returns a promise, but there is both an await keyword IN the function and when using the function. For example:

async goto() {
    await this.page.goto('https://playwright.dev');
  }

Above is a simple method used in Page Object Model pattern, page.goto returns a promise, so we await it…..which makes sense, but to use that you also have to await via:

await playwrightDev.goto();

So I understand the syntax, because the goto function is async. However I guess I don’t really understand "why" we have to do this. More specifically why the function has to be async. Because the inner command of the function is already waiting for a promise, why does the function itself need to be async. Since it won’t return until the innermost command is done anyways?

Hopefully what I am asking makes sense. I understand the syntax but not WHY it is like this.

2

Answers


  1. Why does the function itself need to be async. Since, it won’t return until the innermost command is done anyways?

    No. The async function does immediately return: it returns a promise for the eventual return value of the body. It executes asynchronously, suspending and later resuming its own execution whenever there is an await, but this does not affect the caller.

    The caller just immediately gets a promise. The caller may choose to continue its own execution, e.g. calling other asynchronous functions and passing all the resulting promises into Promise.all, or it may call .then() on the promise, or it may fire-and-forget the promise. Or it may await it to suspend its own execution until the called function is done.

    Login or Signup to reply.
  2. 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 awaited or chained with then. As soon as a single promise is not awaited, 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).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search