JavaScript – Async Iterator & Generator


前言

要看懂这篇请先看下面几篇

Async Iterator (es2018)

es6 推出的 Iterator + for...of 非常好用, 但是它只能执行同步代码. 不支持异步编程.

顾名思义 Async Iterator 就是 Iterator 的 Async 版本. 它支持异步编程.

我们来看看 sync 和 async Iterator 的对比

sync Iterable

const iterable: Iterable = {
  [Symbol.iterator]() {
    let index = 0;
    return {
      next() {
        index++;
        if (index === 10) {
          return {
            done: true,
            value: '',
          };
        }
        return { done: false, value: 'value' + index };
      },
    };
  },
};

for (const value of iterable) {
  console.log('value', value);
}

async Iterable

const asyncIterable: AsyncIterable = {
  [Symbol.asyncIterator]() {
    let index = 0;
    return {
      async next() {
        return new Promise((resolve) => {
          setTimeout(() => {
            index++;
            if (index === 10) {
              resolve({
                done: true,
                value: '',
              });
            }
            return resolve({ done: false, value: 'value' + index });
          }, 1000);
        });
      },
    };
  },
};
(async () => {
  const asyncIterator = asyncIterable[Symbol.asyncIterator]();
  const { value, done } = await asyncIterator.next();
  console.log([value, done]);
  for await (const value of asyncIterable) {
    console.log('value', value);
  }
})();

和 sync iterable 的区别是

1. 属性是 Symbol.asyncIterator

2. iterator next 返回的是一个 promise, promise 的返回才是 done 和 value

3. 遍历的方式是 for await ... of

你可能会认为, sync iterable 也可以返回 promise 丫.

的确. 在  里有提到如何用 Generator + Iterator + Promise + 自执行 Generator 来实现异步编程 (async await 语法糖的背后的原理)

它的 iterator next value 返回的就是 promise. 但不要忘了, 还有 done 属性呢? 这个总不能返回 Promise 了丫. 所以还是有细微区别的. 

Async Generator (es2018)

和 Async Iterator 一样概念. Generator 也有 Async 版本.

function delayAsync(time: number, value: string) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(value);
    }, time);
  });
}
async function* myGenerator() {
  yield delayAsync(1000, 'a');
  console.log('do something');
  yield delayAsync(1000, 'b');
}
(async () => {
  const asyncIterator = myGenerator();
  for await (const value of asyncIterator) {
    console.log(value);
  }
  // 1000ms after log a
  // do something
  // another 1000ms after log b
})();

和 sync Generator 的区别是

1. async function* 多了 async 关键字开头

2. yield 返回 Promise

3. 可以用 for await...of 遍历

for...of + Promise.all 对比 for await...of

当有一堆 promises 想 loop 的时候, 在 for await...of 出现前. 通常是搭配 Promise.all 来使用的

const promises = [delayAsync(1000, 'a'), delayAsync(2000, 'b')];

(async () => {
  for (const value of await Promise.all(promises)) {
    console.log(value);
  }

  for await (const value of promises) {
    console.log(value);
  }
})();

几时知识点:

1. 当 delayAsync 调用时, setTimeout 马山就执行了. 所以下面这 2 个 timeout 是一起 start 的哦

const promises = [delayAsync(1000, 'a'), delayAsync(2000, 'b')];

2. 

for (const value of await Promise.all(promises)) {
    console.log(value);
}

await Promise.all 返回 array 后才进入 for...of. 所以在 2 秒钟后 log a 和 log b 会马上执行 (同步, 中间没有间隔时间)

3.

for await (const value of promises) {
  console.log(value);
}

和 for...of 不同, 它会先去 loop, 所以会先拿出第一个 promise, 等待. 1 秒钟后就会执行 log a.

然后再拿第 2 个 promise. 这时 promise2 已经过去 1 秒了, 所以再 1 秒钟后就会只是 log b.

一定要搞清楚它们执行的顺序和时机哦.

相关