Skip to content

Rolldown 中的顶层 await(TLA)

背景知识:

Rolldown 如何处理 TLA

目前,rolldown 支持 TLA 的原则是:我们会在打包后让它可用,但不会 100% 保留与原始代码完全一致的语义。

当前规则是:

  • 如果你的输入包含 TLA,那么它只能以 esm 格式打包和输出。
  • 禁止 require TLA 模块。

从并发到顺序

rolldown 中 TLA 的一个缺点是,它会把原始代码的行为从并发改为顺序。它仍然能保证相对顺序,但确实会降低执行速度,并且如果原始代码依赖并发,可能会导致执行失败。

cluster_before打包前(并发)cluster_after打包后(顺序)b_mainmain.jsimport tla1, tla2b_allPromise.all([  tla1,  tla2])b_main->b_allb_tla1tla1.jsawait ...b_all->b_tla1b_tla2tla2.jsawait ...b_all->b_tla2b_done均已解析b_tla1->b_doneb_tla2->b_donea_tla1await tla1a_tla2await tla2a_tla1->a_tla2thena_mainconsole.log(  foo1, foo2)a_tla2->a_mainthen
cluster_before打包前(并发)cluster_after打包后(顺序)b_mainmain.jsimport tla1, tla2b_allPromise.all([  tla1,  tla2])b_main->b_allb_tla1tla1.jsawait ...b_all->b_tla1b_tla2tla2.jsawait ...b_all->b_tla2b_done均已解析b_tla1->b_doneb_tla2->b_donea_tla1await tla1a_tla2await tla2a_tla1->a_tla2thena_mainconsole.log(  foo1, foo2)a_tla2->a_mainthen

一个真实世界中的例子可能如下所示

js
// main.js
import { bar } from './sync.js';
import { foo1 } from './tla1.js';
import { foo2 } from './tla2.js';
console.log(foo1, foo2, bar);

// tla1.js

export const foo1 = await Promise.resolve('foo1');

// tla2.js

export const foo2 = await Promise.resolve('foo2');

// sync.js

export const bar = 'bar';

打包之后,它会变成

js
// tla1.js
const foo1 = await Promise.resolve('foo1');

// tla2.js
const foo2 = await Promise.resolve('foo2');

// sync.js
const bar = 'bar';

// main.js
console.log(foo1, foo2, bar);

你可以看到,在打包后的代码中,promise foo1foo2 是顺序解析的,而在原始代码中,它们是并发解析的。

TLA 规范仓库里有一个非常 好的例子,它解释了 TLA 的工作心理模型

js
import { a } from './a.mjs';
import { b } from './b.mjs';
import { c } from './c.mjs';

console.log(a, b, c);

可以被认为在反糖化后等同于如下代码:

js
import { a, promise as aPromise } from './a.mjs';
import { b, promise as bPromise } from './b.mjs';
import { c, promise as cPromise } from './c.mjs';

export const promise = Promise.all([aPromise, bPromise, cPromise]).then(() => {
  console.log(a, b, c);
});

然而,在 rolldown 中,打包后它看起来会像这样:

js
import { a, promise as aPromise } from './a.mjs';
import { b, promise as bPromise } from './b.mjs';
import { c, promise as cPromise } from './c.mjs';

await aPromise;
await bPromise;
await cPromise;

console.log(a, b, c);