ES9 中的 async iterator——async/await 和 for-await-of 的进一步演进
随着 JavaScript 的发展,异步编程已经成为了前端开发中不可避免的一部分。ES6 的 Promise 和 Generator 等新特性为异步编程带来了新的思路和方式,但是在实际使用中仍然存在一些不便之处。ES9 中引入了 async iterator,它实现了异步迭代器的概念,为异步编程提供了更加便捷的方法。
async iterator 的出现,主要是为了解决在异步编程中遍历异步数据的问题。在 ES6 中,我们可以使用 Generator 函数来实现异步迭代器,但是 Generator 函数需要手动控制异步流程,代码可读性和可维护性并不理想。而 async iterator 则是在 Generator 的基础上进一步封装,提供了更加简洁和易用的方式。
async iterator 的基本用法如下:
// www.javascriptcn.com code example
async function* myAsyncGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = myAsyncGenerator();
gen.next().then(console.log); // { value: 1, done: false }
gen.next().then(console.log); // { value: 2, done: false }
gen.next().then(console.log); // { value: 3, done: false }
gen.next().then(console.log); // { value: undefined, done: true }可以看到,async iterator 的语法和 Generator 函数基本相同,只是在函数声明前添加了 async 关键字,表示该函数返回一个异步迭代器。异步迭代器和普通迭代器的区别在于,异步迭代器的 next 方法返回的是一个 Promise 对象。
除了 next 方法,async iterator 还可以实现 Symbol.asyncIterator 方法,使得该对象可以被 for-await-of 循环遍历。for-await-of 循环是 for-of 循环的异步版本,可以遍历异步迭代器返回的 Promise 对象。
// www.javascriptcn.com code example
async function* myAsyncGenerator() {
yield 1;
yield 2;
yield 3;
}
async function main() {
for await (const value of myAsyncGenerator()) {
console.log(value);
}
}
main(); // 1 2 3需要注意的是,for-await-of 循环只能遍历异步迭代器返回的 Promise 对象,如果迭代器返回的是普通对象,则需要使用 for-of 循环。
async iterator 的应用场景非常广泛,比如读取文件、网络请求等异步操作。下面是一个读取文件的示例:
// www.javascriptcn.com code example
import fs from 'fs';
async function* readFile(path) {
const stream = fs.createReadStream(path, { encoding: 'utf8' });
for await (const chunk of stream) {
yield chunk;
}
}
async function main() {
for await (const chunk of readFile('./test.txt')) {
console.log(chunk);
}
}
main();上面的代码中,readFile 函数返回一个异步迭代器,使用 for-await-of 循环遍历文件的每个块。由于文件读取是异步操作,所以必须使用异步迭代器来处理。
在实际开发中,我们可以结合 Promise、async/await 和 async iterator 等特性,编写出更加简洁和易读的异步代码。下面是一个网络请求的示例:
// www.javascriptcn.com code example
async function* fetchAsync(url) {
const res = await fetch(url);
const reader = res.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
yield value;
}
}
async function main() {
const data = [];
for await (const chunk of fetchAsync('https://www.example.com')) {
data.push(chunk);
}
const result = new TextDecoder().decode(data.reduce((a, b) => a.concat(b)));
console.log(result);
}
main();上面的代码中,fetchAsync 函数返回一个异步迭代器,使用 for-await-of 循环遍历网络请求返回的每个块。由于网络请求是异步操作,所以必须使用异步迭代器来处理。最后,我们将所有块拼接起来,并使用 TextDecoder 解码成字符串。
总的来说,async iterator 是异步编程的一大利器,它提供了更加简洁和易用的方式来遍历异步数据。在实际开发中,我们可以结合 Promise、async/await 和 async iterator 等特性,编写出更加简洁和易读的异步代码。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d19ab3a941bf713436365b