当 async await 遇上 iterator

63 阅读1分钟

关于 async await

async/await被称为JS异步终极解决方案。它既能够用同步的方式来书写异步代码,又得到底层的语法支持,无需借助任何第三方库。
Promise 核心思想:Promise 的 then 的注册微任务队列和执行是分离的。
注册 => 完全遵循 JS 和 Promise 的代码的执行过程。
执行 => 先同步,再微任务,再宏任务。

forEach 中用 await 产生的异步问题

举个 🌰:

async function test() {
    let arr = [4, 2, 1];
    arr.forEach(async (item) => {
        const res = await handle(item);
        console.log(res);
    });
    console.log('结束');
}
function handle(x) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(x);
        }, 1000 * x);
    });
}
test();
// 期望输出:
// 4
// 2
// 1
// 结束

// 实际输出:
// 结束
// 1
// 2
// 4

利用for...of就能轻松解决

async function test1() {
    let arr = [4, 2, 1];
    for (const item of arr) {
        const res = await handle(item);
        console.log(res);
    }
    console.log('结束');
}
// 4
// 2
// 1
// 结束

原理——Iterator

for...of 并不像 forEach 那么简单粗暴的方式去遍历执行,而是采用一种特别的手段——迭代器去遍历。
对于数组来讲,它是一种可迭代数据类型——原生具有[Symbol.iterator]属性数据类型为可迭代数据类型,如数组、类数组(如 arguments、NodeList)、Set 和 Map。

可迭代对象可以通过迭代器进行遍历:

let arr = [4, 2, 1];
// 这就是迭代器
let iterator = arr[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

// {value: 4, done: false}
// {value: 2, done: false}
// {value: 1, done: false}
// {value: undefined, done: true}
async function test() {
    let arr = [4, 2, 1];
    let iterator = arr[Symbol.iterator]();
    let res = iterator.next();
    while (!res.done) {
        let value = res.value;
        console.log(value);
        await handle(value);
        res = iterator.next();
    }
    console.log('结束');
}
// 4
// 2
// 1
// 结束