生成器是两个事物的组合 - anIterator
和 an Observer
。
迭代器
迭代器是在调用时返回一个可迭代的东西,这是您可以迭代的东西。从 ES6 开始,所有集合(Array、Map、Set、WeakMap、WeakSet)都符合 Iterable 契约。
生成器(迭代器)是生产者。在迭代中,消费者PULL
从生产者那里获得价值。
例子:
function *gen() { yield 5; yield 6; }
let a = gen();
每当您调用a.next()
时,您实际上是pull
从迭代器中获取值并pause
在 处执行yield
。下次您调用a.next()
时,执行将从先前暂停的状态恢复。
观察者
生成器也是一个观察者,您可以使用它将一些值发送回生成器。用例子更好地解释。
function *gen() {
document.write('<br>observer:', yield 1);
}
var a = gen();
var i = a.next();
while(!i.done) {
document.write('<br>iterator:', i.value);
i = a.next(100);
}
在这里,您可以看到它yield 1
的使用方式类似于计算结果为某个值的表达式。它计算的值是作为参数发送给a.next
函数调用的值。
因此,第一次i.value
将是产生的第一个值 ( 1
),当继续迭代到下一个状态时,我们使用 将一个值发送回生成器a.next(100)
。
你可以在 Node.JS 的什么地方使用它?
生成器广泛用于spawn
(来自 taskJS 或 co)函数,其中函数接收生成器并允许我们以同步方式编写异步代码。这并不意味着异步代码被转换为同步代码/同步执行。这意味着我们可以编写看起来像sync
但内部仍然是async
.
同步被阻塞;异步正在等待。编写阻塞的代码很容易。PULLing 时,赋值位置出现值。PUSHing时,值出现在回调的参数位置
当您使用迭代器时,您PULL
将获得来自生产者的价值。当您使用回调时,生产者PUSH
将值设置为回调的参数位置。
var i = a.next() // PULL
dosomething(..., v => {...}) // PUSH
在这里,您从回调中提取值,a.next()
第二个v => {...}
是回调,并将一个值PUSH
编入v
回调函数的参数位置。
使用这种 pull-push 机制,我们可以像这样编写异步编程,
let delay = t => new Promise(r => setTimeout(r, t));
spawn(function*() {
// wait for 100 ms and send 1
let x = yield delay(100).then(() => 1);
console.log(x); // 1
// wait for 100 ms and send 2
let y = yield delay(100).then(() => 2);
console.log(y); // 2
});
所以,看上面的代码,我们正在编写看起来像它的异步代码blocking
(yield 语句等待 100 毫秒然后继续执行),但实际上它是waiting
. 生成器的pause
andresume
属性使我们能够做到这一点。
它是如何工作的 ?
spawn 函数用于yield promise
从生成器中提取承诺状态,等待承诺被解决,然后将解决的值推回生成器,以便它可以使用它。
立即使用
因此,使用生成器和 spawn 函数,您可以清理 NodeJS 中的所有异步代码,使其看起来和感觉上是同步的。这将使调试变得容易。代码看起来也很整洁。
顺便说一句,这是 ES2017 原生的 JavaScript - as async...await
. 但是您现在可以使用库中定义的 spawn 函数在 ES2015/ES6 和 ES2016 中使用它们 - taskjs、co 或 bluebird