同步遍历器

  1. function idMaker() {
  2. let index = 0;
  3. return {
  4. next: function() {
  5. return { value: index++, done: false };
  6. }
  7. };
  8. }
  9. const it = idMaker();
  10. it.next().value // 0
  11. it.next().value // 1
  12. it.next().value // 2
  13. // ...
  14. var obj = {
  15. [Symbol.iterator]: function () {
  16. return {
  17. index: 0,
  18. next: function () {
  19. return { value: index++, done: false}
  20. }
  21. }
  22. }
  23. }
  24. var it = obj[Symbol.iterator]();
  25. it.next();

这里隐含着一个规定,it.next()方法必须是同步的,只要调用就必须立刻返回值。
目前的解决方法是,将异步操作包装成 Thunk 函数或者 Promise 对象,即next()方法返回值的value属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而done属性则还是同步产生的。

  1. function idMaker() {
  2. let index = 0;
  3. return {
  4. next: function() {
  5. return {
  6. value: new Promise(resolve => setTimeout(() => resolve(index++), 1000)),
  7. done: false
  8. };
  9. }
  10. };
  11. }
  12. const it = idMaker();
  13. it.next().value.then(o => console.log(o)) // 1
  14. it.next().value.then(o => console.log(o)) // 2
  15. it.next().value.then(o => console.log(o)) // 3

异步遍历器

异步遍历器的最大的语法特点,就是调用遍历器的next方法,返回的是一个 Promise 对象。

  1. asyncIterator
  2. .next()
  3. .then(
  4. ({ value, done }) => /* ... */
  5. );

一个对象的同步遍历器的接口,部署在Symbol.iterator属性上面
对象的异步遍历器接口,部署在Symbol.asyncIterator属性上面

  1. const asyncIterable = createAsyncIterable(['a', 'b']);
  2. const asyncIterator = asyncIterable[Symbol.asyncIterator]();
  3. asyncIterator
  4. .next()
  5. .then(iterResult1 => {
  6. console.log(iterResult1); // { value: 'a', done: false }
  7. return asyncIterator.next();
  8. })
  9. .then(iterResult2 => {
  10. console.log(iterResult2); // { value: 'b', done: false }
  11. return asyncIterator.next();
  12. })
  13. .then(iterResult3 => {
  14. console.log(iterResult3); // { value: undefined, done: true }
  15. });

异步遍历器的next方法是可以连续调用的,不必等到上一步产生的 Promise 对象resolve以后再调用。这种情况下,next方法会累积起来,自动按照每一步的顺序运行下去。

  1. async function runner() {
  2. const writer = openFile('someFile.txt');
  3. writer.next('hello');
  4. writer.next('world');
  5. await writer.return();
  6. }
  7. runner();

也可以把所有的next方法放在Promise.all方法里面。

  1. const asyncIterable = createAsyncIterable(['a', 'b']);
  2. const asyncIterator = asyncIterable[Symbol.asyncIterator]();
  3. const [{value: v1}, {value: v2}] = await Promise.all([
  4. asyncIterator.next(), asyncIterator.next()
  5. ]);
  6. console.log(v1, v2); // a b

for await …of

for await...of循环,则是用于遍历异步的 Iterator 接口。

  1. async function f() {
  2. for await (const x of createAsyncIterable(['a', 'b'])) {
  3. console.log(x);
  4. }
  5. }
  6. // a
  7. // b

如果next方法返回的 Promise 对象被rejectfor await...of就会报错,要用try...catch捕捉。

  1. async function () {
  2. try {
  3. for await (const x of createRejectingIterable()) {
  4. console.log(x);
  5. }
  6. } catch (e) {
  7. console.error(e);
  8. }
  9. }

注意,for await...of循环也可以用于同步遍历器。

  1. (async function () {
  2. for await (const x of ['a', 'b']) {
  3. console.log(x);
  4. }
  5. })();
  6. // a
  7. // b

异步 Generator函数

yield* 语句