Javascript 异步发电机中由于承诺的非并行等待而导致的减速
我正在使用生成器和Bluebird编写代码,我有以下几点:Javascript 异步发电机中由于承诺的非并行等待而导致的减速,javascript,async-await,promise,generator,bluebird,Javascript,Async Await,Promise,Generator,Bluebird,我正在使用生成器和Bluebird编写代码,我有以下几点: var async = Promise.coroutine; function Client(request){ this.request = request; } Client.prototype.fetchCommentData = async(function* (user){ var country = yield countryService.countryFor(user.ip); var dat
var async = Promise.coroutine;
function Client(request){
this.request = request;
}
Client.prototype.fetchCommentData = async(function* (user){
var country = yield countryService.countryFor(user.ip);
var data = yield api.getCommentDataFor(user.id);
var notBanned = yield authServer.authenticate(user.id);
if (!notBanned) throw new AuthenticationError(user.id);
return {
country: country,
comments: data,
notBanned: true
};
});
<-client service->
countryFor..
''--..
''--..
''--.. country server sends response
..--''
..--''
..--''
getCommentDataFor
''--..
''--..
''--..
''--.. comment service returns response
..--''
..--''
..--''
authenticate
''--..
''--..
''--.. authentication service returns
..--''
..--''
..--''
Generator done.
但是,这有点慢,我觉得我的应用程序等待I/O的时间太长了,而且它不是并行的。如何提高应用程序的性能
对于
countryFor
+400对于getCommentDataFor
+600对于authenticate
而言,总响应时间为800,因此总计1800ms,这是一个很大的问题。您花费了太多时间等待来自不同来源的I/O
在普通的promise代码中,您会使用promise.all
,然而,人们倾向于编写等待生成器请求的代码。您的代码执行以下操作:
var async = Promise.coroutine;
function Client(request){
this.request = request;
}
Client.prototype.fetchCommentData = async(function* (user){
var country = yield countryService.countryFor(user.ip);
var data = yield api.getCommentDataFor(user.id);
var notBanned = yield authServer.authenticate(user.id);
if (!notBanned) throw new AuthenticationError(user.id);
return {
country: country,
comments: data,
notBanned: true
};
});
<-client service->
countryFor..
''--..
''--..
''--.. country server sends response
..--''
..--''
..--''
getCommentDataFor
''--..
''--..
''--..
''--.. comment service returns response
..--''
..--''
..--''
authenticate
''--..
''--..
''--.. authentication service returns
..--''
..--''
..--''
Generator done.
这是人们第一次使用发电机时经常犯的错误
ascii艺术无耻地取自Kris Kowal的Q-Connection正如蓝鸟文档中提到的,您需要注意不要在一系列中产生
此代码有3个yield
表达式,每个表达式都停止执行,直到特定的承诺得到解决。代码将连续创建和执行每个异步任务
要并行等待多个任务,您应该产生一系列承诺。这将等待所有这些问题解决,然后返回一个结果值数组。使用ES6解构分配可以得到简洁的代码:
Client.prototype.fetchCommentData = async(function* (user){
var [county, data, notBanned] = yield [
// a single yield only: ^^^^^
countryService.countryFor(user.ip),
api.getCommentDataFor(user.id),
authServer.authenticate(user.id)
];
if (!notBanned)
throw new AuthenticationError(user.id);
return {
country: country,
comments: data,
notBanned: true
};
});
Benjamin Gruenbaum的答案是正确的,但是它完全失去了生成器方面的功能,当您尝试并行运行多个事物时,这种情况往往会发生。但是,您可以使用yield
关键字使这项工作正常进行。我还使用了一些额外的ES6功能,如和:
如果您不想使用这些额外的ES6功能:
Client.prototype.fetchCommentData = async(function* (user){
var country = countryService.countryFor(user.ip);
var data = api.getCommentDataFor(user.id);
var notBanned = authServer.authenticate(user.id).then(function(val){
if(!val) throw new AuthenticationError(user.id);
});
var values = yield Promise.all([country, data, notBanned]);
return {
country: values[0],
data: values[1],
notBanned: values[2]
};
});
+1不仅为了这个答案的清晰,而且为了第三人称引用。@Bergi在generators中执行返回
实际上就像屈服
一样,只有完成
标志也设置为true,所以从技术上讲它使用Generator。我同意发电机不再有用了。我问了这个问题,因为我看到人们一直在发电机上错误地使用这个用例,发电机的一个更复杂的例子觉得cruft不值得教导,我仍然愿意接受改进建议。是countryService.countryFor(user.ip)
同步网络吗?我看不到任何回调或承诺会允许它异步。@BenjaminGruenbaum:我会给出一个答案:-)@Bergi一定要这样做:)+1尽管节点11没有这样的分解结构Bluebird是否支持生成一个开箱即用的承诺数组?@rane:在当前版本中看起来不像。请看最后一个添加此功能的示例,如果您不喜欢,可以使用Promise。所有我认为下面的示例最好使用Promise.props
来表达,如上所示。您是对的。我只是坚持ES6 API的承诺,因为这是我真正知道的。我们能不能想出一个更好的标题,类似于“异步生成器中并行运行承诺”?@Bergi,一定要这样做。只是我不喜欢“运行承诺”这个短语,而且我还想加入性能方面的东西。你能想出更好的方法吗?是的,承诺在任何程度上都不是“运行”的,但我教人们代码和回答问题的时间越长,我就越不在乎确切的术语,而更看重有用性。这里的目标是让人们知道生成器在这些场景中可能会很慢,并让人们知道一个常见的性能缺陷,任何能够更好地达到或实现该目标的东西都是积极的IMO@Bergi!NotBanked表示用户被禁止?B/c然后返回notbanked:true,则相反,否?
Client.prototype.fetchCommentData = async(function* (user){
var country = countryService.countryFor(user.ip);
var data = api.getCommentDataFor(user.id);
var notBanned = authServer.authenticate(user.id).then(function(val){
if(!val) throw new AuthenticationError(user.id);
});
// after each async operation finishes, reassign the actual values to the variables
[country, data, notBanned] = yield Promise.all([country, data, notBanned]);
return { country, data, notBanned };
});
Client.prototype.fetchCommentData = async(function* (user){
var country = countryService.countryFor(user.ip);
var data = api.getCommentDataFor(user.id);
var notBanned = authServer.authenticate(user.id).then(function(val){
if(!val) throw new AuthenticationError(user.id);
});
var values = yield Promise.all([country, data, notBanned]);
return {
country: values[0],
data: values[1],
notBanned: values[2]
};
});