由于非平行等待asynchronous生成器中的承诺而导致速度放慢

我正在使用生成器和蓝鸟编写代码,我有以下几点:

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 }; }); 

但是,这是一种缓慢,我觉得我的应用程序正在等待太多的I / O,它不是并行的。 我怎样才能提高我的应用程序的性能?

对于countryFor + 400来说,对于getCommentDataFor + 600来说,总的响应时间是800,因此在总共1800毫秒内是很多的。

您花费太多时间等待来自不同来源的I / O。

在正常的承诺代码中,你会使用Promise.all ,但是,人们倾向于编写代码来等待发生器的请求。 您的代码执行以下操作:

 <-client service-> countryFor.. ''--.. ''--.. ''--.. country server sends response ..--'' ..--'' ..--'' getCommentDataFor ''--.. ''--.. ''--.. ''--.. comment service returns response ..--'' ..--'' ..--'' authenticate ''--.. ''--.. ''--.. authentication service returns ..--'' ..--'' ..--'' Generator done. 

相反,它应该是这样做的:

 <-client service-> countryFor.. commentsFor..''--.. authenticate..''--..''--.. ''--..''--..''--.. country server sends response ''--..--''.. comment service returns response ..--''..--''.. authentication service returns response ..--''..--''.. ..--''..--''..--'' ..--''..--'' ..--'' Generator done 

简而言之,所有的I / O应该在这里并行完成。

要解决这个问题,我会使用Promise.propsPromise.props需要一个对象,并等待其所有属性来解决(如果他们是承诺)。

记住 – 发电机和承诺混合和匹配很好,你只需承诺:

 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); }); return Promise.props({ // wait for all promises to resolve country : country, comments : data, notBanned: notBanned }); }); 

这是人们第一次使用发电机时常犯的一个错误。

ascii艺术无耻地采取从Q连接由Kris Kowal

正如它在Promise.coroutine的Bluebird文档中提到的Promise.coroutine ,您需要注意不要在一系列中产生。

 var county = yield countryService.countryFor(user.ip); var data = yield api.getCommentDataFor(user.id); var notBanned = yield authServer.authenticate(user.id); 

这个代码有3个yieldexpression式,每个expression式停止执行,直到特定的promise被解决。 代码将连续创build并执行每个asynchronous任务。

为了等待多个任务并行,你应该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关键字来完成这个工作。 我也使用一些额外的ES6function,如解构赋值和对象初始化缩写 :

 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 }; }); 

如果你不想使用这些额外的ES6function:

 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] }; });