Skip to content

Rolldown 中的顶层 await(TLA)

背景知识:

Rolldown 如何处理 TLA

现阶段,Rolldown 支持 TLA 的原则是:确保代码在打包后能够运行,但不保证百分之百保留原始代码的语义。

目前的规则如下:

  • 如果输入包含 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_tla2然后a_mainconsole.log(  foo1, foo2)a_tla2->a_main然后
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_tla2然后a_mainconsole.log(  foo1, foo2)a_tla2->a_main然后

下面是一个实际示例:

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);

Was this page helpful?