资讯专栏INFORMATION COLUMN

ES2018 新特征之:异步迭代器 for-await-of

klivitamJ / 2283人阅读

摘要:不幸的是,迭代器不能用来表示这样的数据源。即使是的迭代器也是不够的,因为它的是异步的,但是迭代器需要同步确定状态。异步迭代器一个异步迭代器就像一个迭代器,除了它的方法返回一个的。

ES2018 新特性

异步迭代器(本文)

正则表达式反向(lookbehind)断言

正则表达式 Unicode 转义

非转义序列的模板字符串

正则表达式 s/dotAll 模式

正则表达式命名捕获组

对象展开运算符

Promise.prototype.finally

1. 概述

在 ECMAScript 2015(ES6) 中 JavaScript 引入了迭代器接口(iterator)用来遍历数据。迭代器对象知道如何每次访问集合中的一项, 并跟踪该序列中的当前位置。在 JavaScript 中迭代器是一个对象,它提供了一个 next() 方法,用来返回序列中的下一项。这个方法返回包含两个属性:donevalue

迭代器对象一旦被创建,就可以反复调用 next()

function makeIterator(array) {
  let nextIndex = 0;  // 初始索引

  // 返回一个迭代器对象,对象的属性是一个 next 方法
  return {
    next: function() {
      if (nextIndex < array.length) {
        // 当没有到达末尾时,返回当前值,并把索引加1
        return { value: array[nextIndex++], done: false };
      }

      // 到达末尾,done 属性为 true
      return {done: true};
    }
  };
}

一旦初始化,next() 方法可以用来依次访问对象中的键值:

const it = makeIterator(["j", "u", "s", "t"]);
it.next().value;  // j
it.next().value;  // u
it.next().value;  // s
it.next().value;  // t
it.next().value;  // undefined
it.next().done;   // true
it.next().value;  // undefined
2. 可迭代对象

一个定义了迭代行为的对象,比如在 for...of 中循环了哪些值。为了实现可迭代,一个对象必须实现 @@iterator 方法,这意味着这个对象(或其原型链中的一个对象)必须具有带 Symbol.iterator 键的属性:

StringArrayTypedArrayMapSet 都内置可迭代对象,因为它们的原型对象都有一个 Symbol.iterator 方法。

const justjavac = {
  [Symbol.iterator]: () => {
    const items = [`j`, `u`, `s`, `t`, `j`, `a`, `v`, `a`, `c`];
    return {
      next: () => ({
        done: items.length === 0,
        value: items.shift()
      })
    }
  }
}

当我们定义了可迭代对象后,就可以在 Array.fromfor...of 中使用这个对象:

[...justjavac];
// ["j", "u", "s", "t", "j", "a", "v", "a", "c"]

Array.from(justjavac)
// ["j", "u", "s", "t", "j", "a", "v", "a", "c"]

new Set(justjavac);
// {"j", "u", "s", "t", "a", "v", "c"}

for (const item of justjavac) {
  console.log(item)
}
// j 
// u 
// s 
// t 
// j 
// a 
// v 
// a 
// c
3. 同步迭代

由于在迭代器方法返回时,序列中的下一个值和数据源的 "done" 状态必须已知,所以迭代器只适合于表示同步数据源。

虽然 JavaScript 程序员遇到的许多数据源是同步的(比如内存中的列表和其他数据结构),但是其他许多数据源却不是。例如,任何需要 I/O 访问的数据源通常都会使用基于事件的或流式异步 API 来表示。不幸的是,迭代器不能用来表示这样的数据源。

(即使是 promise 的迭代器也是不够的,因为它的 value 是异步的,但是迭代器需要同步确定 "done" 状态。)

为了给异步数据源提供通用的数据访问协议,我们引入了 AsyncIterator 接口,异步迭代语句(for-await-of)和异步生成器函数。

4. 异步迭代器

一个异步迭代器就像一个迭代器,除了它的 next() 方法返回一个 { value, done } 的 promise。如上所述,我们必须返回迭代器结果的 promise,因为在迭代器方法返回时,迭代器的下一个值和“完成”状态可能未知。

我们修改一下之前的代码:

 const justjavac = {
-  [Symbol.iterator]: () => {
+  [Symbol.asyncIterator]: () => {
     const items = [`j`, `u`, `s`, `t`, `j`, `a`, `v`, `a`, `c`];
     return {
-      next: () => ({
+      next: () => Promise.resolve({
         done: items.length === 0,
         value: items.shift()
       })
     }
   }
 }

好的,我们现在有了一个异步迭代器,代码如下:

const justjavac = {
  [Symbol.asyncIterator]: () => {
    const items = [`j`, `u`, `s`, `t`, `j`, `a`, `v`, `a`, `c`];
    return {
      next: () => Promise.resolve({
        done: items.length === 0,
        value: items.shift()
      })
    }
  }
}

我们可以使用如下代码进行遍历:

for await (const item of justjavac) {
  console.log(item)
}

如果你遇到了 SyntaxError: for await (... of ...) is only valid in async functions and async generators 错误,那是因为 for-await-of 只能在 async 函数或者 async 生成器里面使用。

修改一下:

(async function(){
  for await (const item of justjavac) {
    console.log(item)
  }
})();
5. 同步迭代器 vs 异步迭代器 5.1 Iterators
// 迭代器
interface Iterator {
    next(value) : IteratorResult;
    [optional] throw(value) : IteratorResult;
    [optional] return(value) : IteratorResult;
}

// 迭代结果
interface IteratorResult {
    value : any;
    done : bool;
}
5.2 Async Iterators
// 异步迭代器
interface AsyncIterator {
    next(value) : Promise;
    [optional] throw(value) : Promise;
    [optional] return(value) : Promise;
}

// 迭代结果
interface IteratorResult {
    value : any;
    done : bool;
}
6. 异步生成器函数

异步生成器函数与生成器函数类似,但有以下区别:

当被调用时,异步生成器函数返回一个对象,"async generator",含有 3 个方法(nextthrow,和return),每个方法都返回一个 Promise,Promise 返回 { value, done }。而普通生成器函数并不返回 Promise,而是直接返回 { value, done }。这会自动使返回的异步生成器对象具有异步迭代的功能。

允许使用 await 表达式和 for-await-of 语句。

修改了 yield* 的行为以支持异步迭代。

示例:

async function* readLines(path) {
  let file = await fileOpen(path);

  try {
    while (!file.EOF) {
      yield await file.readLine();
    }
  } finally {
    await file.close();
  }
}

函数返回一个异步生成器(async generator)对象,可以用在 for-await-of 语句中。

7. 实现

Chakra - 暂未支持

JavaScriptCore - Safari Tech Preview 40

SpiderMonkey - Firefox 57

V8 - Chrome 63

Polyfills

Facebook 的 Regenerator 项目为 AsyncIterator 接口提供了一个 polyfill,将异步生成器函数变成返回 AsyncIterator 的对象 ECMAScript 5 函数。Regenerator 还不支持 for-await-of 异步迭代语法。

Babylon parser 项目支持异步生成器函数和 for- await-of 语句(v6.8.0+)。你可以使用它的 asyncGenerators 插件。

require("babylon").parse("code", {
  sourceType: "module",
  plugins: [
    "asyncGenerators"
  ]
});

另外,从 6.16.0 开始,异步迭代被包含在 Babel 的 "babel-plugin-transform-async-generator-functions" 下以及 babel-preset-stage-3

require("babel-core").transform("code", {
  plugins: [
    "transform-async-generator-functions"
  ]
});

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92934.html

相关文章

  • ES2018 特征:非转义序列的模板字符串

    摘要:新特性异步迭代器正则表达式反向断言正则表达式转义非转义序列的模板字符串本文正则表达式模式正则表达式命名捕获组对象展开运算符这个提案由负责,目前已经进入,本提案是的一部分。因此标准移除了对在带标签的模版字符串中转义序列的语法限制。 ES2018 新特性 异步迭代器 正则表达式反向(lookbehind)断言 正则表达式 Unicode 转义 非转义序列的模板字符串(本文) 正则表达式...

    stackfing 评论0 收藏0
  • ES2018 特征:正则表达式 dotAll 模式

    摘要:或者使用来匹配空白字符增加标志在最新的规范中,为的正则表达式增加了一个新的标志用来表示。标志用于指定多行输入字符串应该被视为多个行。标志只影响和标志只影响目前在正则表示中所有修饰符的含义实现 ES2018 新特性 异步迭代器 正则表达式反向(lookbehind)断言 正则表达式 Unicode 转义 非转义序列的模板字符串 正则表达式 s/dotAll 模式(本文) 正则表达式命...

    Joyven 评论0 收藏0
  • 每个JavaScript开发人员都应该知道的ES2018功能(译文)

    摘要:为了使程序员能够一次一个地处理集合中的元素,引入了迭代器接口。迭代器使用该方法获取对象属性名称的数组,然后将其分配给常量。迭代器的缺点是它们不适合表示异步数据源。每次循环时,都会调用迭代器的方法,它返回一个。 前言 原文地址:https://css-tricks.com/new-es2018-features-every-javascript-developer-should-kno...

    leonardofed 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<