简化/详细说明 Nick Sotiros 的回答(我认为这很棒),我认为最好描述一下如何使用 yield 开始编码。
在我看来,使用yield 的最大优势在于它可以消除我们在代码中看到的所有嵌套回调问题。一开始很难看出是怎么回事,这就是为什么我决定写这个答案(为了我自己,也希望是其他人!)
它的实现方式是引入协程的概念,这是一个可以自愿停止/暂停的功能,直到它得到它需要的东西。在 javascript 中,这由function* 表示。只有function*函数可以使用yield。
这是一些典型的 javascript:
loadFromDB('query', function (err, result) {
// Do something with the result or handle the error
})
这很笨拙,因为现在您的所有代码(显然需要等待这个loadFromDB 调用)都需要在这个丑陋的回调中。这很糟糕有几个原因......
- 所有代码都缩进一级
- 你有这个目标
}),你需要随时跟踪它
- 所有这些额外的
function (err, result) 行话
- 不清楚你这样做是为了给
result赋值
另一方面,有了yield,所有这一切都可以在nice co-routine框架的帮助下一行完成。
function* main() {
var result = yield loadFromDB('query')
}
所以现在你的 main 函数将在需要等待变量和加载的东西时在必要的地方产生。但是现在,为了运行它,你需要调用一个normal(非协程函数)。一个简单的协程框架就可以解决这个问题,所以你只需要运行这个:
start(main())
并且定义了开始(来自 Nick Sotiro 的回答)
function start(routine, data) {
result = routine.next(data);
if(!result.done) {
result.value(function(err, data) {
if(err) routine.throw(err); // continue next iteration of routine with an exception
else start(routine, data); // continue next iteration of routine normally
});
}
}
现在,您可以拥有更易读、易于删除且无需摆弄缩进、函数等的漂亮代码。
一个有趣的发现是,在这个例子中,yield 实际上只是一个关键字,您可以将其放在带有回调的函数之前。
function* main() {
console.log(yield function(cb) { cb(null, "Hello World") })
}
将打印“Hello World”。因此,您实际上可以通过简单地创建相同的函数签名(不带 cb)并返回 function (cb) {} 来将任何回调函数转换为使用 yield,如下所示:
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
希望有了这些知识,您可以编写更简洁、更易读的代码easy to delete!