Async.js – ETIMEDOUT和callback已经被调用

当我运行index.js时,我不断收到ETIMEDOUTECONNRESET错误,后面跟着一个Callback was already called错误。

起初我以为是因为在调用onEachLimitItemcallbackonEachLimitItem之前我没有包含return 。 所以我将其包含在asynchronous多个callback文档中 。 仍然没有解决它。 我也尝试删除错误事件,并在错误事件中删除onEachLimit的callback,但都没有工作。 我已经看过围绕Callback already calledCallback already called问题的其他SO问题,但是由于他们不关心stream,我没有find解决scheme。

我的理解是,如果stream遇到像ECONNRESET这样的错误,它将返回错误事件中的callback,并继续到下一个stream,但似乎并不是这样。 几乎看起来,如果错误自行解决,即它重新连接,并尝试再次发送到Azure错误的蒸汽,它的工作,然后触发“完成”事件,我们得到了Callback already calledCallback already called

我是否正确处理stream事件中的callback?

 var Q = require('q'); var async = require('async'); var webshot = require('webshot'); var Readable = require('stream').Readable; var azure = require('azure-storage'); var blob = azure.createBlobService('123', '112244'); var container = 'awesome'; var countries = [ 'en-us', 'es-us', 'en-au', 'de-at', 'pt-br', 'en-ca', 'fr-ca', 'cs-cz', 'ar-ly', 'es-ve', 'da-dk', 'fi-fi', 'de-de', 'hu-hu', 'ko-kr', 'es-xl', 'en-my', 'nl-nl', 'en-nz', 'nb-no', 'nn-no', 'pl-pl', 'ro-ro', 'ru-ru', 'ca-es', 'es-es', 'eu-es', 'gl-es', 'en-gb', 'es-ar', 'nl-be', 'bg-bg', 'es-cl', 'zh-cn', 'es-co', 'es-cr', 'es-ec', 'et-ee', 'fr-fr', 'el-gr', 'zh-hk', 'en-in', 'id-id', 'en-ie', 'he-il', 'it-it', 'ja-jp', 'es-mx', 'es-pe', 'en-ph' ]; var uploadStreamToStorage = function (fileName, stream, onEachLimitItem) { var readable = new Readable().wrap(stream); var writeable = blob.createWriteStreamToBlockBlob(container, fileName); readable.pipe(writeable); writeable.on('error', function (error) { return onEachLimitItem.call(error); }); writeable.on('finish', function () { onEachLimitItem.call(null); }); }; var takeIndividualScreenshot = function (ID, country, onEachLimitItem) { var fileName = ID + '-' + country + '.jpg'; var url = 'https://example.com/' + country + '/' + ID; webshot(url, function (error, stream) { if (error) { throw 'Screenshot not taken'; } uploadStreamToStorage(fileName, stream, onEachLimitItem); }); }; var getAllCountriesOfId = function (ID) { var deferred = Q.defer(); var limit = 5; function onEachCountry(country, onEachLimitItem) { takeIndividualScreenshot(ID, country, onEachLimitItem); } async.eachLimit(countries, limit, onEachCountry, function (error) { if (error) { deferred.reject(error); } deferred.resolve(); }); return deferred.promise; }; var createContainer = function () { var df = Q.defer(); var self = this; blob.createContainerIfNotExists(this.container, this.containerOptions, function (error) { if (error) { df.reject(error); } df.resolve(self.container); }); return df.promise; }; createContainer() .then(function () { return getAllCountriesOfId('211007'); }) .then(function () { return getAllCountriesOfId('123456'); }) .fail(function (error) { log.info(error); }); 

在这里输入图像描述

你已经知道,你让你的callback被调用两次。 问题是; 你是否想要在迭代stream时停止所有错误,或者是否想要累积stream中的所有错误?

有多种方法来捕获和处理您已经在做的错误,但是因为您没有将导致您的数据stream的额外调用的错误对象丢到致命的错误。

代码中的实际问题是由于您返回的范围所致。 当您处理错误并尝试返回callback并暂停脚本执行时,小时返回的范围对于streamerror handling程序而言是本地的,而不是全局脚本,因此脚本将继续并捕获移动到下一个有效的stream。

 writeable.on('error', function (error) { // This 'return' is in the local scope of 'writable.on('error')' return onEachLimitItem.call(error); }); 

它也许可以设置一个数组,然后处理该函数本地作用域之外的错误。 即

 // Set the array's scope as global to the writable.on() error var errResults = []; writeable.on('error', function (error) { // Push the local scoped 'error' into the global scoped 'errResults' array errResults.push(error); }); writeable.on('finish', function () { // Are there any errors? return (errResults.length > 0) ? onEachLimitItem.call(errors) : onEachLimitItem.call(null); }); 

以上只是解决问题的方法之一。

我不知道你是否已经阅读了Joyent提供的error handling帮助(原始node.js语言支持者),但是在处理错误时它应该给你一个好的select。

https://www.joyent.com/developers/node/design/errors