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

docs(asyce): edit async Generator

This commit is contained in:
ruanyf 2017-07-02 17:15:38 +08:00
parent f86bff91c7
commit e35fcfda61
3 changed files with 112 additions and 37 deletions

View File

@ -742,14 +742,17 @@ async function f() {
```javascript
let body = '';
async function f() {
for await(const data of req) body += data;
const parsed = JSON.parse(body);
console.log('got', parsed);
}
```
上面代码中,`req`是一个 asyncIterable 对象,用来异步读取数据。可以看到,使用`for await...of`循环以后,代码会非常简洁。
如果`next`方法返回的Promise对象被`reject`,那么就要用`try...catch`捕捉。
如果`next`方法返回的 Promise 对象被`reject``for await...of`就会报错,要用`try...catch`捕捉。
```javascript
async function () {
@ -781,6 +784,19 @@ async function () {
在语法上,异步 Generator 函数就是`async`函数与 Generator 函数的结合。
```javascript
async function* gen() {
yield 'hello';
}
const genObj = gen();
genObj.next().then(x => console.log(x));
// { value: 'hello', done: false }
```
上面代码中,`gen`是一个异步 Generator 函数,执行后返回一个异步 Iterator 对象。对该对象调用`next`方法,返回一个 Promise 对象。
下面是另一个例子。
```javascript
async function* readLines(path) {
let file = await fileOpen(path);
@ -795,14 +811,18 @@ async function* readLines(path) {
}
```
上面代码中,异步操作前面使用`await`关键字标明,即`await`后面的操作,应该返回 Promise 对象。凡是使用`yield`关键字的地方,就是`next`方法的停下来的地方,它后面的表达式的值(即`await file.readLine()`的值),会作为`next()`返回对象的`value`属性,这一点是同步 Generator 函数一致的。
上面代码中,异步操作前面使用`await`关键字标明,即`await`后面的操作,应该返回 Promise 对象。凡是使用`yield`关键字的地方,就是`next`方法的停下来的地方,它后面的表达式的值(即`await file.readLine()`的值),会作为`next()`返回对象的`value`属性,这一点是同步 Generator 函数一致的。
可以像下面这样,使用上面代码定义的异步 Generator 函数。
异步 Generator 函数内部,能够同时使用`await``yield`命令。可以这样理解,`await`命令用于将外部操作产生的值输入函数内部,`yield`命令用于将函数内部的值输出。
上面代码定义的异步 Generator 函数的用法如下。
```javascript
(async function () {
for await (const line of readLines(filePath)) {
console.log(line);
}
})()
```
异步 Generator 函数可以与`for await...of`循环结合起来使用。
@ -815,7 +835,7 @@ async function* prefixLines(asyncIterable) {
}
```
`yield`命令依然是立刻返回的,但是返回的是一个 Promise 对象。
异步 Generator 函数的返回值是一个异步 Iterator即每次调用它的`next`方法,会返回一个 Promise 对象,也就是说,跟在`yield`命令后面的,应该是一个 Promise 对象。
```javascript
async function* asyncGenerator() {
@ -824,9 +844,34 @@ async function* asyncGenerator() {
yield 'Result: '+ result; // (B)
console.log('Done');
}
const ag = asyncGenerator();
ag.next().then({value, done} => {
// ...
})
```
上面代码中,调用`next`方法以后,会在`B`处暂停执行,`yield`命令立刻返回一个Promise对象。这个Promise对象不同于`A``await`命令后面的那个 Promise 对象。主要有两点不同,一是`A`处的Promise对象`resolve`以后产生的值,会放入`result`变量;二是`B`处的Promise对象`resolve`以后产生的值,是表达式`'Result ' + result`的值;二是`A`处的 Promise 对象一定先于`B`处的 Promise 对象`resolve`
上面代码中,`ag``asyncGenerator`函数返回的异步 Iterator 对象。调用`ag.next()`以后,`asyncGenerator`函数内部的执行顺序如下。
1. 打印出`Start`
2. `await`命令返回一个 Promise 对象,但是程序不会停在这里,继续往下执行。
3. 程序在`B`处暂停执行,`yield`命令立刻返回一个 Promise 对象,该对象就是`ag.next()`的返回值。
4. `A``await`命令后面的那个 Promise 对象 resolved产生的值放入`result`变量。
5. `B`处的 Promise 对象 resolved`then`方法指定的回调函数开始执行,该函数的参数是一个对象,`value`的值是表达式`'Result ' + result`的值,`done`属性的值是`false`
A 和 B 两行的作用类似于下面的代码。
```javascript
return new Promise((resolve, reject) => {
doSomethingAsync()
.then(result => {
resolve({
value: 'Result: '+result,
done: false,
});
});
});
```
如果异步 Generator 函数抛出错误,会被 Promise 对象`reject`,然后抛出的错误被`catch`方法捕获。
@ -840,7 +885,7 @@ asyncGenerator()
.catch(err => console.log(err)); // Error: Problem!
```
注意,普通的 async 函数返回的是一个 Promise 对象,而异步 Generator 函数返回的是一个异步Iterator对象。基本上可以这样理解`async`函数和异步 Generator 函数,是封装异步操作的两种方法,都用来达到同一种目的。区别在于,前者自带执行器,后者通过`for await...of`执行,或者自己编写执行器。下面就是一个异步 Generator 函数的执行器。
注意,普通的 async 函数返回的是一个 Promise 对象,而异步 Generator 函数返回的是一个异步 Iterator 对象。可以这样理解async 函数和异步 Generator 函数,是封装异步操作的两种方法,都用来达到同一种目的。区别在于,前者自带执行器,后者通过`for await...of`执行,或者自己编写执行器。下面就是一个异步 Generator 函数的执行器。
```javascript
async function takeAsync(asyncIterable, count = Infinity) {
@ -855,7 +900,7 @@ async function takeAsync(asyncIterable, count=Infinity) {
}
```
上面代码中异步Generator函数产生的异步遍历器会通过`while`循环自动执行,每当`await iterator.next()`完成,就会进入下一轮循环。
上面代码中,异步 Generator 函数产生的异步遍历器,会通过`while`循环自动执行,每当`await iterator.next()`完成,就会进入下一轮循环。一旦`done`属性变为`true`,就会跳出循环,异步遍历器执行结束。
下面是这个自动执行器的一个使用实例。
@ -875,7 +920,18 @@ f().then(function (result) {
})
```
异步 Generator 函数出现以后JavaScript就有了四种函数形式普通函数、async 函数、Generator 函数和异步 Generator 函数。请注意区分每种函数的不同之处。
异步 Generator 函数出现以后JavaScript 就有了四种函数形式普通函数、async 函数、Generator 函数和异步 Generator 函数。请注意区分每种函数的不同之处。基本上,如果是一系列按照顺序执行的异步操作(比如读取文件,然后写入新内容,再存入硬盘),可以使用 async 函数;如果是一系列产生相同数据结构的异步操作(比如一行一行读取文件),可以使用异步 Generator 函数。
异步 Generator 函数也可以通过`next`方法的参数,接收外部传入的数据。
```javascript
const writer = openFile('someFile.txt');
writer.next('hello'); // 立即执行
writer.next('world'); // 立即执行
await writer.return(); // 等待写入结束
```
上面代码中,`openFile`是一个异步 Generator 函数。`next`方法的参数,向该函数内部的操作传入数据。每次`next`方法都是同步执行的,最后的`await`命令用于等待整个写入操作结束。
最后,同步的数据结构,也可以使用异步 Generator 函数。
@ -901,6 +957,7 @@ async function* gen1() {
}
async function* gen2() {
// result 最终会等于 2
const result = yield* gen1();
}
```

View File

@ -1101,6 +1101,24 @@ let aClone = { ...a };
let aClone = Object.assign({}, a);
```
上面的例子只是拷贝了对象实例的属性,如果想完整克隆一个对象,还拷贝对象原型的属性,可以采用下面的写法。
```javascript
// 写法一
const clone1 = {
__proto__: Object.getPrototypeOf(obj),
...obj
};
// 写法二
const clone2 = Object.assign(
Object.create(Object.getPrototypeOf(obj)),
obj
);
```
上面代码中,写法一的`__proto__`属性在非浏览器的环境不一定部署,因此推荐使用写法二。
扩展运算符可以用于合并两个对象。
```javascript
@ -1134,6 +1152,16 @@ let newVersion = {
上面代码中,`newVersion`对象自定义了`name`属性,其他属性全部复制自`previousVersion`对象。
如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值。
```javascript
let aWithDefaults = { x: 1, y: 2, ...a };
// 等同于
let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
// 等同于
let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);
```
与数组的扩展运算符一样,对象的扩展运算符后面可以跟表达式。
```javascript
@ -1150,14 +1178,10 @@ const obj = {
// { a: 1 }
```
如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值
如果扩展运算符的参数是`null``undefined`,这两个值会被忽略,不会报错
```javascript
let aWithDefaults = { x: 1, y: 2, ...a };
// 等同于
let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
// 等同于
let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);
let emptyObject = { ...null, ...undefined }; // 不报错
```
扩展运算符的参数对象之中,如果有取值函数`get`,这个函数是会执行的。
@ -1182,15 +1206,9 @@ let runtimeError = {
};
```
如果扩展运算符的参数是`null``undefined`,这两个值会被忽略,不会报错。
```javascript
let emptyObject = { ...null, ...undefined }; // 不报错
```
## Object.getOwnPropertyDescriptors()
ES5一个`Object.getOwnPropertyDescriptor`方法返回某个对象属性的描述对象descriptor
ES5 一个`Object.getOwnPropertyDescriptor`方法返回某个对象属性的描述对象descriptor
```javascript
var obj = { p: 'a' };