网易首页 > 网易号 > 正文 申请入驻

ES9的新特性:异步遍历Async iteration

0
分享至

简介

在ES6中,引入了同步iteration的概念,随着ES8中的Async操作符的引用,是不是可以在一异步操作中进行遍历操作呢?

今天要给大家讲一讲ES9中的异步遍历的新特性Async iteration。

异步遍历

在讲解异步遍历之前,我们先回想一下ES6中的同步遍历。

根据ES6的定义,iteration主要由三部分组成:

  1. Iterable

先看下Iterable的定义:

interface Iterable {
[Symbol.iterator]() : Iterator;

Iterable表示这个对象里面有可遍历的数据,并且需要实现一个可以生成Iterator的工厂方法。

  1. Iterator

interface Iterator {
next() : IteratorResult;

可以从Iterable中构建Iterator。Iterator是一个类似游标的概念,可以通过next访问到IteratorResult。

  1. IteratorResult

IteratorResult是每次调用next方法得到的数据。

interface IteratorResult {
value: any;
done: boolean;

IteratorResult中除了有一个value值表示要获取到的数据之外,还有一个done,表示是否遍历完成。

下面是一个遍历数组的例子:

> const iterable = ['a', 'b'];
> const iterator = iterable[Symbol.iterator]();
> iterator.next()
{ value: 'a', done: false }
> iterator.next()
{ value: 'b', done: false }
> iterator.next()
{ value: undefined, done: true }

但是上的例子遍历的是同步数据,如果我们获取的是异步数据,比如从http端下载下来的文件,我们想要一行一行的对文件进行遍历。因为读取一行数据是异步操作,那么这就涉及到了异步数据的遍历。

加入异步读取文件的方法是readLinesFromFile,那么同步的遍历方法,对异步来说就不再适用了:

//不再适用
for (const line of readLinesFromFile(fileName)) {
console.log(line);
}

也许你会想,我们是不是可以把异步读取一行的操作封装在Promise中,然后用同步的方式去遍历呢?

想法很好,不过这种情况下,异步操作是否执行完毕是无法检测到的。所以方法并不可行。

于是ES9引入了异步遍历的概念:

  1. 可以通过Symbol.asyncIterator来获取到异步iterables中的iterator。

  2. 异步iterator的next()方法返回Promises对象,其中包含IteratorResults。

所以,我们看下异步遍历的API定义:

interface AsyncIterable {
[Symbol.asyncIterator]() : AsyncIterator;
}
interface AsyncIterator {
next() : Promise;
}
interface IteratorResult {
value: any;
done: boolean;
}

我们看一个异步遍历的应用:

const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
asyncIterator.next()
.then(iterResult1 => {
console.log(iterResult1); // { value: 'a', done: false }
return asyncIterator.next();
})
.then(iterResult2 => {
console.log(iterResult2); // { value: 'b', done: false }
return asyncIterator.next();
})
.then(iterResult3 => {
console.log(iterResult3); // { value: undefined, done: true }
});

其中createAsyncIterable将会把一个同步的iterable转换成一个异步的iterable,我们将会在下面一小节中看一下到底怎么生成的。

这里我们主要关注一下asyncIterator的遍历操作。

因为ES8中引入了Async操作符,我们也可以把上面的代码,使用Async函数重写:

async function f() {
const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
console.log(await asyncIterator.next());
// { value: 'a', done: false }
console.log(await asyncIterator.next());
// { value: 'b', done: false }
console.log(await asyncIterator.next());
// { value: undefined, done: true }
}
异步iterable的遍历

使用for-of可以遍历同步iterable,使用 for-await-of 可以遍历异步iterable。

async function f() {
for await (const x of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// Output:
// a
// b

注意,await需要放在async函数中才行。

如果我们的异步遍历中出现异常,则可以在 for-await-of 中使用try catch来捕获这个异常:

function createRejectingIterable() {
return {
[Symbol.asyncIterator]() {
return this;
},
next() {
return Promise.reject(new Error('Problem!'));
},
};
}
(async function () {
try {
for await (const x of createRejectingIterable()) {
console.log(x);
}
} catch (e) {
console.error(e);
// Error: Problem!
}
})();

同步的iterable返回的是同步的iterators,next方法返回的是{value, done}。

如果使用 for-await-of 则会将同步的iterators转换成为异步的iterators。然后返回的值被转换成为了Promise。

如果同步的next本身返回的value就是Promise对象,则异步的返回值还是同样的promise。

也就是说会把:Iterable>转换成为AsyncIterable,如下面的例子所示:

async function main() {
const syncIterable = [
Promise.resolve('a'),
Promise.resolve('b'),
];
for await (const x of syncIterable) {
console.log(x);
}
}
main();

// Output:
// a
// b

上面的例子将同步的Promise转换成异步的Promise。

async function main() {
for await (const x of ['a', 'b']) {
console.log(x);
}
}
main();

// Output:
// c
// d

上面的例子将同步的常量转换成为Promise。可以看到两者的结果是一样的。

异步iterable的生成

回到上面的例子,我们使用createAsyncIterable(syncIterable)将syncIterable转换成了AsyncIterable。

我们看下这个方法是怎么实现的:

async function* createAsyncIterable(syncIterable) {
for (const elem of syncIterable) {
yield elem;
}
}

上面的代码中,我们在一个普通的generator function前面加上async,表示的是异步的generator。

对于普通的generator来说,每次调用next方法的时候,都会返回一个object {value,done} ,这个object对象是对yield值的封装。

对于一个异步的generator来说,每次调用next方法的时候,都会返回一个包含object {value,done} 的promise对象。这个object对象是对yield值的封装。

因为返回的是Promise对象,所以我们不需要等待异步执行的结果完成,就可以再次调用next方法。

我们可以通过一个Promise.all来同时执行所有的异步Promise操作:

const asyncGenObj = createAsyncIterable(['a', 'b']);
const [{value:v1},{value:v2}] = await Promise.all([
asyncGenObj.next(), asyncGenObj.next()
]);
console.log(v1, v2); // a b

在createAsyncIterable中,我们是从同步的Iterable中创建异步的Iterable。

接下来我们看下如何从异步的Iterable中创建异步的Iterable。

从上一节我们知道,可以使用for-await-of 来读取异步Iterable的数据,于是我们可以这样用:

async function* prefixLines(asyncIterable) {
for await (const line of asyncIterable) {
yield '> ' + line;
}
}

在generator一文中,我们讲到了在generator中调用generator。也就是在一个生产器中通过使用yield*来调用另外一个生成器。

同样的,如果是在异步生成器中,我们可以做同样的事情:

async function* gen1() {
yield 'a';
yield 'b';
return 2;
async function* gen2() {
const result = yield* gen1();
// result === 2

(async function () {
for await (const x of gen2()) {
console.log(x);
}
})();
// Output:
// a
// b

如果在异步生成器中抛出异常,这个异常也会被封装在Promise中:

async function* asyncGenerator() {
throw new Error('Problem!');
asyncGenerator().next()
.catch(err => console.log(err)); // Error: Problem!
异步方法和异步生成器

异步方法是使用async function 声明的方法,它会返回一个Promise对象。

function中的return或throw异常会作为返回的Promise中的value。

(async function () {
return 'hello';
.then(x => console.log(x)); // hello

(async function () {
throw new Error('Problem!');
})()
.catch(x => console.error(x)); // Error: Problem!

异步生成器是使用 async function * 申明的方法。它会返回一个异步的iterable。

通过调用iterable的next方法,将会返回一个Promise。异步生成器中yield 的值会用来填充Promise的值。如果在生成器中抛出了异常,同样会被Promise捕获到。

async function* gen() {
yield 'hello';
const genObj = gen();
genObj.next().then(x => console.log(x));
// { value: 'hello', done: false }

本文作者:flydean程序那些事 本文链接:http://www.flydean.com/es9-async-iteration/ 本文来源:flydean的博客 欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
新加坡防长写打油诗总结"香会" 现场用中文朗读

新加坡防长写打油诗总结"香会" 现场用中文朗读

看看新闻Knews
2026-05-31 18:36:09
半导体不香了?大基金套现210亿,不是跑路,是去接下一个孩子

半导体不香了?大基金套现210亿,不是跑路,是去接下一个孩子

户外钓鱼哥阿旱
2026-06-02 17:36:14
哈马斯军官混进医院当医生,重建武装时遭以军击毙

哈马斯军官混进医院当医生,重建武装时遭以军击毙

桂系007
2026-06-01 19:48:39
23岁摩洛哥王储拒绝“吻手礼”再出圈,几名男子试图亲吻其右手被拒

23岁摩洛哥王储拒绝“吻手礼”再出圈,几名男子试图亲吻其右手被拒

极目新闻
2026-06-02 19:49:03
医生提醒:肺癌最危险信号,不是咳嗽,而是频繁出现这5种异常

医生提醒:肺癌最危险信号,不是咳嗽,而是频繁出现这5种异常

健康之光
2026-06-02 21:00:06
6月锦鲤附体|四大星座迎来全年高光,财运人缘双丰收✨

6月锦鲤附体|四大星座迎来全年高光,财运人缘双丰收✨

别人都叫我阿螫
2026-06-02 23:03:15
林志玲被封杀?被传出在内娱彻底“消失”

林志玲被封杀?被传出在内娱彻底“消失”

霹雳炮
2026-06-02 23:17:38
刚刚 | 天津三区预警!雷雨大风冰雹!个别点10级以上!

刚刚 | 天津三区预警!雷雨大风冰雹!个别点10级以上!

天津广播
2026-06-02 19:46:21
几大天灾齐聚东北,乱成了一锅粥 | 地球知识局

几大天灾齐聚东北,乱成了一锅粥 | 地球知识局

地球知识局
2026-06-02 20:41:12
终于知道为什么领导那么喜欢上班了,网友曝光领导抽屉都是性用品

终于知道为什么领导那么喜欢上班了,网友曝光领导抽屉都是性用品

灯锦年
2026-06-01 00:35:03
儿子啃老 20 年离世,父亲整理存折,发现密码背后的辛酸往事

儿子啃老 20 年离世,父亲整理存折,发现密码背后的辛酸往事

朝暮书屋
2025-05-15 21:31:28
2017年,邓小平弟弟去世享年106岁,临终前立下与哥哥同样的遗嘱

2017年,邓小平弟弟去世享年106岁,临终前立下与哥哥同样的遗嘱

大运河时空
2026-06-02 10:15:03
杨瀚森翻译:开拓者队已经通知我,后续不会雇我随队,杨瀚森表态

杨瀚森翻译:开拓者队已经通知我,后续不会雇我随队,杨瀚森表态

生活新鲜市
2026-05-29 04:46:55
“卷王”中产妈妈:“我每天只花10块钱、睡3小时,打4份工供女儿学琴。老公在家躺平,如今过成这样……”

“卷王”中产妈妈:“我每天只花10块钱、睡3小时,打4份工供女儿学琴。老公在家躺平,如今过成这样……”

阅读第一
2026-06-02 10:10:59
被申请破产!又一网红品牌跌落神坛,曾在李佳琦直播间爆卖

被申请破产!又一网红品牌跌落神坛,曾在李佳琦直播间爆卖

品牌观察官
2026-06-02 17:42:26
除了俄乌战争,还有一场伤亡更大的战争已经打了两年多

除了俄乌战争,还有一场伤亡更大的战争已经打了两年多

商业模式桑博士
2026-05-29 07:49:46
他是赢球功臣!7中5+打中三分,楼明为他鼓掌,球迷:早该用他了

他是赢球功臣!7中5+打中三分,楼明为他鼓掌,球迷:早该用他了

南海浪花
2026-06-02 22:26:52
24小时已过,普京政府准时断供,航油不卖中国,欧盟启动应急方案

24小时已过,普京政府准时断供,航油不卖中国,欧盟启动应急方案

爱下厨的阿酾
2026-06-02 17:12:48
万万没想到,西贝翻车的路,爱奇艺又走了一遍,网友群嘲:没底线

万万没想到,西贝翻车的路,爱奇艺又走了一遍,网友群嘲:没底线

离离言几许
2026-05-31 21:21:29
访华结束,武契奇带着遗憾离京:签了几十份大单,唯独没有歼10CE

访华结束,武契奇带着遗憾离京:签了几十份大单,唯独没有歼10CE

素衣读史
2026-05-30 21:26:52
2026-06-03 00:20:49
flydean程序那些事
flydean程序那些事
最通俗的解读,最深刻的干货!
356文章数 438关注度
往期回顾 全部

科技要闻

烧掉千亿后,美团、阿里、京东谁先止血?

头条要闻

演员魏宗万去世 曾在94版《三国演义》中饰演"司马懿"

头条要闻

演员魏宗万去世 曾在94版《三国演义》中饰演"司马懿"

体育要闻

1米74的业余联赛替补,在英超踢中卫

娱乐要闻

奚梦瑶何猷君补办婚礼超幸福

财经要闻

智元和宇树的“暗战”愈演愈烈

汽车要闻

星途神秘新车轮廓曝光 又一款性能SUV要来了?

态度原创

教育
时尚
房产
家居
军事航空

教育要闻

“你知道几号高考吗?”18岁纹身女孩的生日美照,诠释了物以类聚

蓝色系下装看着清爽不闷,裤子、裙子都凉快,随便穿都不出错

房产要闻

5200巨量投资曝光!未来五年,海南格局大变!

家居要闻

流线型轮廓 包容多元身形

军事要闻

伊朗媒体新发布最高领袖照片

无障碍浏览 进入关怀版