Iterator-Observer 接口
发电机是两件事的组合 - 一个 Iterator
和一个 Observer
。
迭代器
迭代器是被调用时返回 iterable
的东西。一个 iterable
是你可以迭代的东西。从 ES6 / ES2015 开始,所有集合(Array, Map, Set, WeakMap, WeakSet)都符合 Iterable 合同。
生成器(迭代器)是生产者。在迭代中,消费者来自生产者的价值。
例:
function *gen() { yield 5; yield 6; }
let a = gen();
每当你调用 a.next()
时,你实际上都是从迭代器中获取价值,而在 yield
中执行的是 tihuan。下次调用 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)
将值发送回生成器。
与生成器进行异步
生成器广泛用于 spawn
(来自 taskJS 或 co)函数,其中函数接收生成器并允许我们以同步方式编写异步代码。这并不意味着异步代码被转换为同步代码/同步执行。这意味着我们可以编写看起来像 sync
的代码,但在内部它仍然是 async
。
同步是阻止; 异步是等待。编写阻塞代码很容易。拉出时,值出现在分配位置。当 PUSHing 时,值出现在回调的参数位置。
当你使用迭代器时,你需要生成器的值。当你使用回调时,生产者 tihuan 将值转换为回调的参数位置。
var i = a.next() // PULL
dosomething(..., v => {...}) // PUSH
在这里,你从 a.next()
中提取值,在第二个中,v => {...}
是回调,并且值被转换为回调函数的参数位置 v
。
使用这种推拉机制,我们可以编写这样的异步编程,
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 语句等待 100ms,然后继续执行),但它实际上是 waiting
。发电机的 pause
和 resume
属性允许我们做这个惊人的技巧。
它是如何工作的?
spawn 函数使用 yield promise
从生成器中拉出 promise 状态,等待 promise 被解析,并将解析后的值推回给生成器,以便它可以使用它。
现在就用它
因此,使用生成器和 spawn 函数,你可以清理 NodeJS 中的所有异步代码,使其看起来和感觉它是同步的。这将使调试变得容易。代码看起来也很整洁。
这个功能将在未来版本的 JavaScript 中出现 - 如 async...await
。但是你现在可以使用库中定义的 spawn 函数在 ES2015 / ES6 中使用它们 - taskjs,co 或 bluebird