几个月前,Deno(Node.js 的继任者)被发布,我在主页上找到了一些有关如何使用它的演示:
import { serve } from "https://deno.land/std@0.69.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
req.respond({ body: "Hello World\n" });
}
突然我的眼睛转向第4行,在 for 之后的 await:“那是什么???”
我从来没有见过这样的东西,我的第一个想法是:这是 deno 做的一个相当酷和奇怪的事情……
想象一下,当我读到有关 deno 的更多信息时,我发现那小段代码实际上是合法的 JavaScript,并且在 Node.js 中也是有效的,而我对此一无所知。
那是什么?为什么我从未见过?我应该在哪里使用?我错过了什么?
如果你有同样的问题,那就好!
这篇文章将尝试回答所有这些问题!
先说重要的:
Iterators(迭代器)
你有见过像这样的东西么?
class RandomNumberGenerator {
[Symbol.iterator]() {
return {
next: () => {
return { value: Math.random() };
},
};
}
}
如果你见过,那太好了,你可以跳到下一部分!
如果还没有,那么让我们深入研究下它在做什么:
RAndomNumberGenerator 正在实现 [Symbol.iterator] 或 @@iterator 方法(当通过 Symbol 属性定义方法时,我们使用双 @@ 引用该方法)。
在对象上定义 [Symbol.iterator] 或 @@iterator 方法时,可以对该对象进行迭代。
由于现在我们将 @@iterator 方法定义为 RandomNumberGenerator 类的实例方法,因此该类的所有实例现在都可以迭代。您现在可以通过运行以下代码对其进行测试:
class RandomNumberGenerator {
[Symbol.iterator]() {
return {
next: () => {
return { value: Math.random() };
},
};
}
}
const rand = new RandomNumberGenerator();
for (const random of rand) {
console.log(random);
if (random < 0.1) break;
}
为了正常工作,@@iterator 方法必须返回一个包含 next 方法的对象,且 next 方法需要返回另一个具有 value 和 done 属性的对象。
value 将包含返回值,done 可以是一个 boolean,如果它被设置为 true 则结束迭代。
虽然 value 是强制性的,但可以如上面的示例中那样省略 done(这使我们可以定义无限的可迭代对象)。
很酷!
现在,我们可以迭代!
这个什么时候有用呢?
我相信它高度依赖于您所创建的业务逻辑的类型。
例如,我极力推荐的 《Node.js 设计模式》一书提供了一个迭代矩阵(您可能已将其定义为数组的数据)。
我也相信这篇文章重点介绍了一些很酷的用法。它定义了一些非常酷的方法,看起来很像python风格。
但是,如果您需要我的诚实和个人见解,我还没有遇到我说:“这对于迭代器来说是一个很好的用例”的情况。
无论如何,让我们回到本文的主题:将 await 添加到 for 循环中还需要什么?
Async Iterators (异步迭代器)
异步迭代器(顾名思义)是我们在上面的示例中所做的异步版本。
想象一下,我们没有返回随机数,而是返回了 promise。那会是什么样的呢?
如果我们更改上面的示例来做到这一点,它将看起来像这样:
const simulateDelay = (val, delay) =>
new Promise((resolve) => setTimeout(() => resolve(val), delay));
class RandomNumberGenerator {
[Symbol.asyncIterator]() {
return {
next: async () => {
return simulateDelay({ value: Math.random() }, 200); //return the value after 200ms of delay
},
};
}
}
const rand = new RandomNumberGenerator();
(async () => {
for await (const random of rand) {
console.log(random);
if (random < 0.1) break;
}
})();
有什么变化?
- 我们首先将Symbol属性更改为
asyncIterator,而不仅仅是iterator - 我们将
next方法设为异步函数。 - 我创建了
simulateDelay函数,该函数返回一个 promise,该 promise 在给定时间后 resolve。 - 我在 for 循环中加了 await
- 我们将循环放在即时调用的函数表达式中,以避免在顶级等待调用时出现问题。(注意:Node.js版本14+中不再需要这个了)
所以我们已经做了一个简单的程序,它能够迭代一个对象,以异步的方式获得它的元素。
如果你知道除了 deno 首页上的例子外任何其他的异步迭代器的实现,请给我电子邮件或评论他们下面!