iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >详解ES9的新特性之异步遍历Async iteration
  • 637
分享到

详解ES9的新特性之异步遍历Async iteration

2024-04-02 19:04:59 637人浏览 八月长安
摘要

目录异步遍历异步iterable的遍历异步iterable的生成异步方法和异步生成器异步遍历 在讲解异步遍历之前,我们先回想一下es6中的同步遍历。 根据ES6的定义,iterati

异步遍历

在讲解异步遍历之前,我们先回想一下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引入了异步遍历的概念:

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

2.异步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 }

以上就是详解ES9的新特性之异步遍历Async iteration的详细内容,更多关于ES9的新特性之异步遍历Async iteration的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解ES9的新特性之异步遍历Async iteration

本文链接: https://www.lsjlt.com/news/127494.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解ES9的新特性之异步遍历Async iteration
    目录异步遍历异步iterable的遍历异步iterable的生成异步方法和异步生成器异步遍历 在讲解异步遍历之前,我们先回想一下ES6中的同步遍历。 根据ES6的定义,iterati...
    99+
    2024-04-02
  • ES9新特性之异步遍历Async iteration的示例分析
    这篇文章主要介绍了ES9新特性之异步遍历Async iteration的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。异步遍历在讲解异步遍历之前,我们先回想一下ES6...
    99+
    2023-06-15
  • ES9中新特性Async iteration的示例分析
    这篇文章将为大家详细讲解有关ES9中新特性Async iteration的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在ES6中,引入了同步iteration的概念,随着ES8中的Async操作...
    99+
    2023-06-14
  • ES9的新特性之正则表达式RegExp详解
    目录简介Numbered capture groupsNamed capture groupsRegExp中Unicode属性的转义lookaround assertiondotAl...
    99+
    2024-04-02
  • JDK7新特性之遍历文件树的示例分析
    这篇文章主要介绍JDK7新特性之遍历文件树的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!有时需要递归遍历一个文件树,比如查找一个文件夹内符合条件的文件,查找某一天创建的文件……。jdk7 nio包提供一个新...
    99+
    2023-06-17
  • Nodejs新特性async和await的使用详解
    目录1.Es6常见语法的使用2.Async、Await和Promise1.Es6常见语法的使用 1.let、const let:是一个块作用域 if (true) { let...
    99+
    2024-04-02
  • python3.9之你应该知道的新特性详解
    目录一、数字类型二、字符串三、列表类型四、for循环一、数字类型 python除了支持原有的int和float类型,新增了支持Decimal或者Fraction。python还内置支...
    99+
    2024-04-02
  • JS面试之console的异步性怎么理解详解
    目录面试题目(字节):答案解析:引用类型结论面试题目(字节): console的异步性怎么理解? 答案解析: console我一直以为是同步执行的,直到在一本书上看到,才了解到con...
    99+
    2022-11-13
    JS面试console异步性 JS console
  • ECharts图表使用及异步加载的特性示例详解
    目录ECharts异步加载ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 c...
    99+
    2022-12-23
    ECharts图表异步加载 ECharts 异步加载
  • 深入理解Java8新特性之Stream API的终止操作步骤
    目录1.写在前面2.终止操作2.1 终止操作之查找与匹配2.2 终止操作之归约与收集1.写在前面 承接了上一篇文章(说完了Stream API的创建方式及中间操作):深入理解Java...
    99+
    2024-04-02
  • Java8新特性之精简的JRE详解_动力节点Java学院整理
    Oracle公司如期发布了Java 8正式版!没有让广大javaer失望。对于一个人来说,18岁是人生的转折点,从稚嫩走向成熟,法律意味着你是完全民事行为能力人,不再收益于未成年人保护法,到今年为止,java也走过了18年,java8是一个...
    99+
    2023-05-31
    java8 新特性 jre
  • Java8新特性之接口中的默认方法和静态方法详解
    目录一、前言二、为什么在 Java 接口中使用默认方法?三、为什么在 Java 接口中使用静态方法?四、场景一:接口中的默认方法五、场景二:接口中的静态方法六、情景三:多重继承 - ...
    99+
    2024-04-02
  • 深入理解Java8新特性之Stream API的创建方式和中间操作步骤
    目录1.什么是StreamAPI?2.Stream API操作的三个步骤2.1 创建Stream2.2 中间操作2.2.1 中间操作之筛选与切片2.2.2 中间操作之映射2.2.3 ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作