方法#0。没有承诺的痛苦生活。然而生活
实际上,您的代码喜欢用承诺重写。相信我,这种重构是你 100% 需要的。但是好的,让我们尝试在不调用承诺的情况下解决这个特定问题——就像一个练习一样。实际上,在 Promise 时代之前,模式是引入了一个特殊的函数来检查我们是否可以认为我们已经完成了。
在您的特定情况下,此类功能是:
function weAreDone() {
return final.hasOwnPropery('x') && final.hasOwnProperty('y')
}
那么我们可以引入asyncFuncDecorator:
function asyncFuncDecorator = function(asyncFunc, asyncFuncHandler) {
return function(doneFunc, doneHandler) {
asyncFunc(asyncFuncHandler);
if (doneFunc()) {
doneHandler();
}
}
}
通过引入这两个函数,您可以编写如下内容:
var foo = function(callback) {
var final = {};
//here goes abovementioned declarations
...
asyncFuncDecorator(asyncFuncOne, function(x) {
final.x = x;
})(weAreDone, callback);
asyncFuncDecorator(asyncFuncTwo, function(y) {
final.y = y;
})(weAreDone, callback);
});
您可以继续努力使这种方法更加灵活和通用,但请再次相信我,
你最终会得到与 promises 非常相似的东西,所以会有更好的 promises ;)
方法 #1。承诺现有功能
如果由于某种原因,您还没有准备好将所有函数从回调样式重写为 Promise,
您可以再次使用装饰器来承诺现有功能。以下是所有现代浏览器中都存在的原生 Promise 的实现方法(替代方案,请查看 this question):
function promisify(asyncCall){
return new Promise(function(resolve,reject){
asyncCall(resolve,reject);
});
}
在这种情况下,你可以用这种方式重写你的代码:
var foo = function(callback) {
//here goes abovementioned declarations
...
Promise.all([promisify(asyncFuncOne), promisify(asyncFuncTwo)]).then(function(data) {
// by the way, I'd rather not to call any variable "final" ))
final.x = data[0];
final.y = data[1];
}).then(callback);
});
并不是说实际上 foo 最好是自己承诺;)
方法 #2。承诺无处不在。从一开始就
值得重申这个想法——一旦你需要在 N 个其他异步函数完成后触发某个函数——在 99% 的情况下,promise 是无与伦比的。几乎总是值得尝试以基于 Promise 的样式重写现有代码。下面是这样的代码的样子
Promise.all([asyncFuncOne(), asyncFuncTwo()]).then(function(data) {
return Promise.resolve({
x: data[0],
y: data[1]
})
}).then(callback);
看看它变得多么好。此外,使用 Promise 的一个常见错误 - 是有一个连续的 then 瀑布 - 检索第一块数据,只有在那之后 - 第二个,在那之后 - 第三个。实际上,您永远不应该这样做,除非您根据您在之前的一个请求中获得的内容来转换在第 N 个请求中收到的数据 - 而只是使用 all 方法。
理解这一点非常重要。这是承诺经常被误解为过于复杂的主要原因之一。
旁注:截至 2014 年 12 月,除了 IE 之外,所有主流现代浏览器都原生支持原生 Promise,并且在 Node.js 中,原生 Promise 支持是从 0.11.13 版本开始的,所以在现实生活中你仍然最可能需要使用 promise 库。有很多 Promise 规范的实现,你可以查看 this page 获取独立 Promise 库的列表,它相当大,我猜最流行的解决方案是 Q 和 bluebird。
方法#3。发电机。我们光明的未来。嗯,可能是
值得一提的是,Firefox、基于 Chromium 的浏览器和 node.js(使用 --harmony_generators 选项调用)事实上支持生成器。因此,事实上,在某些情况下,生成器可以在生产代码中使用,并且实际上已经在使用。只是如果你正在编写一个通用的网络应用程序,你应该知道这种方法,但你可能暂时不会使用它。因此,您可以使用 js 中的生成器允许您通过 yield/iterator.next() 调用双向通信这一事实。在那种情况下。
function async(gen) {
var it = gen();
var state = it.next();
var next = function() {
if (state.done) {
return state.value;
};
state.value(function(res) {
state = it.next(res);
next();
});
}
next();
}
async(function* () {
var res = {
x: yield asyncFuncOne,
y: yield asyncFuncTwo
}
callback(res);
});
实际上,已经有几十个库可以为您完成这个生成器包装工作。
您可以阅读有关此方法和相关库的更多信息here。