1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 10:22:23 +00:00

docs(async): add top-level await

This commit is contained in:
ruanyf 2019-06-08 14:50:08 +08:00
parent 98e3bcb2df
commit c00389ae77
2 changed files with 152 additions and 1 deletions

View File

@ -43,7 +43,7 @@ function idMaker() {
}
```
上面代码中,`next()`方法返回的是一个 Promise 对象,这样就不行,不符合 Iterator 协议。也就是说Iterator 协议里面`next()`方法只能包含同步操作。
上面代码中,`next()`方法返回的是一个 Promise 对象,这样就不行,不符合 Iterator 协议,只要代码里面包含异步操作都不行。也就是说Iterator 协议里面`next()`方法只能包含同步操作。
目前的解决方法是,将异步操作包装成 Thunk 函数或者 Promise 对象,即`next()`方法返回值的`value`属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而`done`属性则还是同步产生的。

View File

@ -684,4 +684,155 @@ async function logInOrder(urls) {
上面代码中,虽然`map`方法的参数是`async`函数,但它是并发执行的,因为只有`async`函数内部是继发执行,外部不受影响。后面的`for..of`循环内部使用了`await`,因此实现了按顺序输出。
## 顶层 await
根据语法规格,`await`命令只能出现在 async 函数内部,否则都会报错。
```javascript
// 报错
const data = await fetch('https://api.example.com');
```
上面代码中,`await`命令独立使用,没有放在 async 函数里面,就会报错。
目前,有一个[语法提案](https://github.com/tc39/proposal-top-level-await),允许在模块的顶层独立使用`await`命令。这个提案的目的,是借用`await`解决模块异步加载的问题。
```javascript
// awaiting.js
let output;
async function main() {
const dynamic = await import(someMission);
const data = await fetch(url);
output = someProcess(dynamic.default, data);
}
main();
export { output };
```
上面代码中,模块`awaiting.js`的输出值`output`,取决于异步操作。我们把异步操作包装在一个 async 函数里面,然后调用这个函数,只有等里面的异步操作都执行,变量`output`才会有值,否则就返回`undefined`
上面的代码也可以写成立即执行函数的形式。
```javascript
// awaiting.js
let output;
(async function main() {
const dynamic = await import(someMission);
const data = await fetch(url);
output = someProcess(dynamic.default, data);
})();
export { output };
```
下面是加载这个模块的写法。
```javascript
// usage.js
import { output } from "./awaiting.js";
function outputPlusValue(value) { return output + value }
console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);
```
上面代码中,`outputPlusValue()`的执行结果,完全取决于执行的时间。如果`awaiting.js`里面的异步操作没执行完,加载进来的`output`的值就是`undefined`
目前的解决方法,就是让原始模块输出一个 Promise 对象,从这个 Promise 对象判断异步操作有没有结束。
```javascript
// awaiting.js
let output;
export default (async function main() {
const dynamic = await import(someMission);
const data = await fetch(url);
output = someProcess(dynamic.default, data);
})();
export { output };
```
上面代码中,`awaiting.js`除了输出`output`,还默认输出一个 Promise 对象async 函数立即执行后,返回一个 Promise 对象),从这个对象判断异步操作是否结束。
下面是加载这个模块的新的写法。
```javascript
// usage.js
import promise, { output } from "./awaiting.js";
function outputPlusValue(value) { return output + value }
promise.then(() => {
console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);
});
```
上面代码中,将`awaiting.js`对象的输出,放在`promise.then()`里面,这样就能保证异步操作完成以后,才去读取`output`
这种写法比较麻烦,等于要求模块的使用者遵守一个额外的使用协议,按照特殊的方法使用这个模块。一旦你忘了要用 Promise 加载,只使用正常的加载方法,依赖这个模块的代码就可能出错。而且,如果上面的`usage.js`又有对外的输出,等于这个依赖链的所有模块都要使用 Promise 加载。
顶层的`await`命令,就是为了解决这个问题。它保证只有异步操作完成,模块才会输出值。
```javascript
// awaiting.js
const dynamic = import(someMission);
const data = fetch(url);
export const output = someProcess((await dynamic).default, await data);
```
上面代码中,两个异步操作在输出的时候,都加上了`await`命令。只有等到异步操作完成,这个模块才会输出值。
加载这个模块的写法如下。
```javascript
// usage.js
import { output } from "./awaiting.js";
function outputPlusValue(value) { return output + value }
console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);
```
上面代码的写法,与普通的模块加载完全一样。也就是说,模块的使用者完全不用关心,依赖模块的内部有没有异步操作,正常加载即可。
这时,模块的加载会等待依赖模块(上例是`awaiting.js`)的异步操作完成,才执行后面的代码,有点像暂停在那里。所以,它总是会得到正确的`output`,不会因为加载时机的不同,而得到不一样的值。
下面是顶层`await`的一些使用场景。
```javascript
// import() 方法加载
const strings = await import(`/i18n/${navigator.language}`);
// 数据库操作
const connection = await dbConnector();
// 依赖回滚
let jQuery;
try {
jQuery = await import('https://cdn-a.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.com/jQuery');
}
```
注意,如果加载多个包含顶层`await`命令的模块,加载命令是同步执行的。
```javascript
// x.js
console.log("X1");
await new Promise(r => setTimeout(r, 1000));
console.log("X2");
// y.js
console.log("Y");
// z.js
import "./x.js";
import "./y.js";
console.log("Z");
```
上面代码有三个模块,最后的`z.js`加载`x.js``y.js`,打印结果是`X1``Y``X2``Z`。这说明,`z.js`并没有等待`x.js`加载完成,再去加载`y.js`
顶层的`await`命令有点像,交出代码的执行权给其他的模块加载,等异步操作完成后,再拿回执行权,继续向下执行。