Introducing top-level-await


https://github.com/hiroppy/slides

import { process } from './some-module.js';

const dynamic = import(computedModuleSpecifier);
const data = fetch(url);

//                             ๐Ÿ‘‡                      ๐Ÿ‘‡
export const output = process((await dynamic).default, await data);

https://github.com/tc39/proposal-top-level-await

Variants


A / B / C(dropped. to the optional constraint)


Currently, TLA refers to variant B.


  • Variant A: TLA blocks tree execution
  • Variant B: TLA doesn't block sibling execution
import a from './a.js'; // this module has TLA
import b from './b.js'; // this module has TLA
import c from './c.js'; // this module has TLA

console.log(a, b, c);

// --------------------------------------------
// Execution Order
async function main() {
  // reduce startup time
  const [a, b, c] = await Promise.all([import('./a.mjs'), import('./b.mjs'), import('./c.mjs')]);

  // it won't be like this(this is variant A)
  // const a = await import('./a.js');
  // const b = await import('./b.js');
  // const c = await import('./c.js');
}

TLA wouldn't block execution in the graph until it had resolved but blocks execution of child nodes in the graph.

TLA occurs during the execution phase of the module graph so all resources have already been fetched and linked.


here for details

Deadlocks


Pay attention to Circular Module Dependencies.
Both modules using TLA halt progress until the other finishes loading.


// ---------- a.js ----------
await import('b');

// implement a hoistable then() to prevent cycles while a module is still evaluating.
export function then(f, r) {
  r('not finished');
};

// remove the rejection
then = null;

// ๐Ÿ™…โ€โ™€๏ธ---------- b.js ----------
await import('a');

// ๐Ÿ™†โ€โ™€๏ธ ---------- b.js ----------
let a;

try {
  a = await import('a');
} catch {
  // do something
}

Userland


  • V8@7.9.49
  • webpack@5.0.0-alpha.15

webpack@next

// ---------- webpack.config.js ----------
module.exports = {
  experiments: {
    topLevelAwait: true,
    importAwait: true,
    importAsync: true,
    asyncWebAssembly: true
  }
};

// ---------- db-connection.js ----------
export const dbCall = async data => {
  await new Promise(r => setTimeout(r, 100));
  return 'fake data';
};

// ---------- main.js ----------
import await { dbCall } from './db-connection.js';
// or const { dbCall } = import('./db-connection.js');

const { createUser } = await UserApi;

db-connection.js is an async module and async modules have different evaluation semantics.

Async modules can't imported with a normal import. They need to be imported with import await.

import() doesn't care about whether a module is an async module or not.

References


The End