我们说我有一个看起来像这样的功能:
var foo = function(callback) {
var final = {};
asyncFuncOne(function(x) {
final.x = x;
});
asyncFuncTwo(function(y) {
final.y = y;
});
callback(final);
});
显然,这并不是我想做的事情(当callback
同时有x和y时调用final
。我有几个问题:
final
?答案 0 :(得分:1)
方法#0。没有承诺的痛苦生活。生活
实际上,你的代码就好像在承诺中重写一样。相信我,这种重构是你100%需要的。但是好吧,让我们尝试解决这个特殊的问题,而不是一直调用承诺 - 就像练习一样。实际上在承诺时代之前,模式是引入一个特殊功能,检查我们是否可以认为我们已经完成了。
在您的特定情况下,此类功能是:
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);
});
你可以继续努力使这种方法更加灵活和普遍,但再次相信我, 你会得到与承诺非常相似的东西,所以更好的承诺;)
方法#1。宣传现有职能
如果出于某种原因,你还没有准备好将所有功能从回调样式重写为承诺, 你可以再次使用装饰器来宣传现有的功能。以下是本地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.all([asyncFuncOne(), asyncFuncTwo()]).then(function(data) {
return Promise.resolve({
x: data[0],
y: data[1]
})
}).then(callback);
看看它变得多好。此外,使用promises的一个常见错误 - 是有一个顺序的thens瀑布 - 只在那之后检索第一块数据 - 第二个,之后 - 第三个。实际上你永远不应该这样做,除非你正在转换第N个请求中收到的数据,具体取决于你之前的一个请求中的内容 - 而只是使用 all 方法。
理解这一点至关重要。这是承诺经常被误解为过于复杂的主要原因之一。
旁白:从14年12月开始,除了IE之外,所有主要的现代浏览器都本机支持本机Promise,并且在Node.js中有本机承诺支持,因为版本0.11.13,所以在现实生活中你仍然是最可能需要使用promise库。有很多Promise规范实现,你可以查看this page的独立承诺库列表,它非常大,最流行的解决方案是,我猜,Q和蓝鸟。方法#3。发电机。我们美好的未来。好吧,可能
值得一提的是,Firefox,基于Chromium的浏览器和node.js(使用--harmony_generators选项调用)事实上支持生成器。因此,事实上,在生产代码中,有些情况下可以使用生成器,并且实际上已经使用了生成器。只是如果你正在编写一个通用的Web应用程序,你应该知道这种方法,但你可能暂时不会使用它。因此,您可以使用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的更多信息。
答案 1 :(得分:0)
final.x
和final.y
设置为final,但在之后发送到callback
,因此,除非回调正在等待,{{
x
未定义。
您可以查看是否有人回复其他人的回复并呼叫回调:
y
您可以嵌套回调,但这会导致在var foo = function(callback) {
var final = {};
asyncFuncOne(function(x) {
final.x = x;
if (typeof final.y !== 'undefined') {
callback(final);
}
});
asyncFuncTwo(function(y) {
final.y = y;
if (typeof final.x !== 'undefined') {
callback(final);
}
});
});
完成之前不会调用asyncfuncTwo
:
asyncfuncOne
然后是Promises。这些是异步的未来,但是并非所有浏览器都支持它们(即IE的所有IE [11及以下])。事实上, 40%的浏览器用户没有使用本机支持Promises的浏览器。这意味着您必须使用polyfill库来支持向页面添加大量文件大小。对于这个简单的问题,在这个给定的时间,我不建议使用Promises来解决这个简单的问题。但是,你一定要仔细阅读它们的使用方法。
如果你想看看它的样子,那就是:
var foo = function(callback) {
var final = {};
asyncFuncOne(function(x) {
final.x = x;
asyncFuncTwo(function(y) {
final.y = y;
callback(final);
});
});
});
注意没有回调,只需使用var asyncFuncOne = function() {
return new Promise(function(resolve, reject) {
// A 500 seconds async op and resolve x as 5
setTimeout(function() { resolve(5); }, 500);
});
};
var asyncFuncTwo = function() {
return new Promise(function(resolve, reject) {
// A 750ms async op and resolve y as 10
setTimeout(function() { resolve(10); }, 750);
});
};
var foo = function() {
var final = {};
return new Promise(function(resolve, reject) {
Promise.all([
asyncFuncOne(),
asyncFuncTwo()
]).then(function(values) {
final.x = values[0];
final.y = values[1];
resolve(final);
});
});
};
foo().then(function(final) {
// After foo()'s Promise has resolved (750ms)
console.log(final.x + ', ' + final.y);
});
。在实际情况中,您还可以使用then
和catch
。在这里阅读更多关于Promises的信息https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise但是,我个人并不认为有必要将它们用于这个单一的特定问题(但是,对于他们自己的问题)。
答案 2 :(得分:0)
另一个解决方案是创建一个setter:
var foo = function (callback) {
var final = {
setter: function(attr,value){
this[attr] = value;
if (this.hasOwnProperty("x") && this.hasOwnProperty("y"))
callback(this);
}
};
asyncFuncOne(function(x) {
final.setter("x", x);
});
asyncFuncTwo(function(y) {
final.setter("y", y);
});
};
答案 3 :(得分:-2)
一个非常糟糕的主意,但我以前不得不使用它,因为我不打算为单个函数导入一个50k的promise库,就是设置一个循环的Timeout来检查看看如果设置了所有必需的变量,然后调用回调。