Search code examples
typescriptasync-awaitpromiseplaywrightdestructuring

Playwright promise.all returning page/context?


So I saw a bit of confused and I was a bit confused what it was doing:

 private async SwitchPage(): Promise<Page> {
        const [newWindow] = await Promise.all([
            await this.context.waitForEvent("page")
        ]);
        await newWindow.waitForLoadState();
        console.log(`switched to new window ${await newWindow.title()}`);
        return newWindow;
    };

I saw this in a Playwright sample demo/project. Im not used to using promise.all in general, so Im a little bit confused what the const [newWindow] exactly is? I'd assume it's the return type of whatever is enclosed in the promise.all block. But according to playwright: https://playwright.dev/docs/api/class-browsercontext#browser-context-wait-for-event It seems to just return a regular object, and not some sort of context or page?

But maybe im missing something here. How is this person chaining waitForLoadState() off what is just a base object and not specifically a browser context?


Solution

  • Promise.all is designed to resolve after all promises in its argument array are resolved. It returns a promise that resolves to an array, which can be destructured. The Promise.all here is pointless for two reasons:

    1. only a single item is in the array and
    2. the item that's in the array isn't a promise because it was already awaited.

    The following simpler code is functionally identical:

    private async SwitchPage(): Promise<Page> {
      const newWindow = await this.context.waitForEvent("page");
      await newWindow.waitForLoadState();
      console.log(`switched to new window ${await newWindow.title()}`);
      return newWindow;
    };
    

    It seems to just return a regular object, and not some sort of context or page?

    waitForEvent() returns a promise that resolves (via await) to an Object, which in this case is a Page instance. The reason for the generic Object typing is because different parameter event names can return different types of objects, not just Pages.

    How is this person chaining waitForLoadState() off what is just a base object and not specifically a browser context?

    It's a Page, which is known to TS because of the return value type Promise<Page>. Pages have .waitForLoadState() and .title() methods, among other things.

    I saw this in a Playwright sample demo/project

    I would hesitate to trust the quality of this sample demo/project, as this usage of Promise.all indicates that the author isn't familiar with promise fundamentals. This is a red flag.


    If you're curious about the destructuring syntax, any function that returns an array, such as an awaited Promise.all, can be destructured:

    const a = [42, 1];
    const [x, y] = a;
    console.log(x, y); // => 42 1

    Here's a more sensible use of Promise.all, following the same principle:

    const sleep = ms => new Promise(resolve =>
      setTimeout(() => resolve(ms), ms)
    );
    
    (async () => {
      const [x, y] = await Promise.all([
        sleep(1000), // note: no `await`!
        sleep(3000),
      ]);
      console.log(x, y); // => 1000, 3000
    })();

    To emphasize that the destructuring syntax is purely cosmetic, the following is functionally the same as above, except without destructuring:

    const sleep = ms => new Promise(resolve =>
      setTimeout(() => resolve(ms), ms)
    );
    
    (async () => {
      const durations = await Promise.all([
        sleep(1000), // note: no `await`!
        sleep(3000),
      ]);
      const x = durations[0];
      const y = durations[1];
      console.log(x, y); // => 1000, 3000
    })();