ES9的新特性:异步遍历Async iteration
ES9的新特性:异步遍历Async iteration
简介
在ES6中,引入了同步iteration的概念,随着ES8中的Async操作符的引用,是不是可以在一异步操作中进行遍历操作呢?
今天要给大家讲一讲ES9中的异步遍历的新特性Async iteration。
异步遍历
在讲解异步遍历之前,我们先回想一下ES6中的同步遍历。
根据ES6的定义,iteration主要由三部分组成:
- Iterable
先看下Iterable的定义:
interface Iterable {
[Symbol.iterator]() : Iterator;
}
Iterable表示这个对象里面有可遍历的数据,并且需要实现一个可以生成Iterator的工厂方法。
- Iterator
interface Iterator {
next() : IteratorResult;
}
可以从Iterable中构建Iterator。Iterator是一个类似游标的概念,可以通过next访问到IteratorResult。
- 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引入了异步遍历的概念:
可以通过Symbol.asyncIterator来获取到异步iterables中的iterator。
异步iterator的next()方法返回Promises对象,其中包含IteratorResults。
所以,我们看下异步遍历的API定义:
interface AsyncIterable {
[Symbol.asyncIterator]() : AsyncIterator;
}
interface AsyncIterator {
next() : Promise<IteratorResult>;
}
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<Promise<T>>
转换成为 AsyncIterable<T>
,如下面的例子所示:
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的博客
欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
ES9的新特性:异步遍历Async iteration的更多相关文章
- javaScript ES7 ES8 ES9 ES10新特性
参考文献: https://tuobaye.com/2018/11/27/%E7%BB%86%E8%A7%A3JavaScript-ES7-ES8-ES9-%E6%96%B0%E7%89%B9%E6% ...
- jdk8中关于操作集合的一些新特性,遍历和排序操作
jdk8增加了不少新的东西,在集合操作这块,就有如 lamda表达式,stream,sort,optional等新的类,主要涉及遍历和排序等方面,新特性提升了不少性能,我们开发就是要拥抱新事物,守着老 ...
- ES9的新特性:正则表达式RegExp
简介 正则表达式是我们做数据匹配的时候常用的一种工具,虽然正则表达式的语法并不复杂,但是如果多种语法组合起来会给人一种无从下手的感觉. 于是正则表达式成了程序员的噩梦.今天我们来看一下如何在ES9中玩 ...
- JDk8的新特性-流和内部iteration
JDK8到今天已经出了好几年了 但是在公司能用到新特性的地方还是很少, 去年的时候当时项目老大要求我们用最新的写法来写Java 刚开始看到用stream写出来的代码一脸懵逼,内心就在想 这是Jav ...
- servlet3.0 新特性——异步处理
Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下: 首先,Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理: 接着,调用业务接口的某些方法,以完成业 ...
- JAVA8新特性--集合遍历之forEach
java中的集合有两种形式Collection<E>,Map<K,V> Collection类型集合 在JAVA7中遍历有一下几种方式:List<String> l ...
- ECMAScript 2018(ES9)新特性简介
目录 简介 异步遍历 Rest/Spread操作符和对象构建 Rest Spread 创建和拷贝对象 Spread和bject.assign() 的区别 正则表达式 promise.finally 模 ...
- 细解JavaScript ES7 ES8 ES9 新特性
题记:本文提供了一个在线PPT版本,方便您浏览 细解JAVASCRIPT ES7 ES8 ES9 新特性 在线PPT ver 本文的大部分内容译自作者Axel Rauschmayer博士的网站,想了解 ...
- ES9新特性
这篇文章主要介绍ES2018(ES9)的新特性, 以及使用方法 JS是一门跨平台的语言, ES6也就是ECMAScript 2015 花费了5年的时间敲定, 是一次非常大的改版, 之后每年都有一个小版 ...
随机推荐
- js 监听ajax请求
function hookSend(hook) { if (!XMLHttpRequest.prototype._oldSend) XMLHttpRequest.prototype._oldSend ...
- c++ readIntger writeIntger
类似CE的read/writeIntger函数(外部) #include <iostream> #include <Windows.h> #include <TlHelp ...
- Flutter: 粘贴板
文档 import 'package:flutter/services.dart'; /// 把文本复制进入粘贴板 Clipboard.setData(ClipboardData(text: &quo ...
- Java 添加 、读取以及删除PPT幻灯片中的视频、音频文件
在PPT中,可以操作很多种元素,如形状.图形.文字.图片.表格等,也可以插入视频或者音频文件,来丰富幻灯片的内容呈现方式.下面将介绍在Java程序中如何来添加视频.音频文件到PPT幻灯片,读取和删除幻 ...
- HBase ——Shell操作
HBase --Shell操作 Q:你觉得HBase是什么? A:一种结构化的分布式数据存储系统,它基于列来存储数据. 基于HBase,可以实现以廉价PC机器集群存储海量数据的分布式数据库的解决方案. ...
- git配置了公钥,在下载项目时为什么还要输入密码
配置git地址:https://www.cnblogs.com/lz0925/p/10794616.html 原文链接:https://blog.csdn.net/xiaomengzi_16/arti ...
- Vuex入门、同步异步存取值进阶版
关注公众号查看原文: 1. vueX介&绍安装 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方 ...
- 如何创建一个Maven项目(eclipse版本)
1 Maven概念 Maven是一个构建项目和管理项目依赖的工具 2 Maven运行原理 这里需要引入两个词汇,叫 本地仓库.中央仓库 本地仓库:就字面意思,存储在自己电脑上的文件夹(需要自己手动创建 ...
- 【资源下载】Linux下的Hi3861一站式鸿蒙开发烧录(附工具)
下载附件 2021春节前夕,华为发布了 HUAWEI DevEco Device Tool 2.0 Beta1,整体提供了异常强大的功能.得知消息后,我在第一时间带着无比兴奋的心情下载尝鲜,但结果却是 ...
- Image Super-Resolution via Sparse Representation——基于稀疏表示的超分辨率重建
经典超分辨率重建论文,基于稀疏表示.下面首先介绍稀疏表示,然后介绍论文的基本思想和算法优化过程,最后使用python进行实验. 稀疏表示 稀疏表示是指,使用过完备字典中少量向量的线性组合来表示某个元素 ...