Async.js - 已经调用了ETIMEDOUT和Callback

时间:2015-10-23 19:30:12

标签: javascript node.js callback promise async.js

我运行ETIMEDOUT时出现ECONNRESETCallback was already called错误,然后出现index.js错误。

起初我以为是因为在调用return回调之前我没有包含onEachLimitItem。所以我按async multiple callbacks documentation列出了它。仍然没有解决它。我还尝试删除错误事件并在错误事件中删除对onEachLimit的回调,但两者都没有奏效。我已经查看围绕Callback already called问题的其他SO问题,但由于他们不关心流,我没有找到解决方案。

我的理解是,如果流遇到类似ECONNRESET的错误,它将在错误事件中返回回调并转到下一个流,但似乎并非如此。似乎错误自行解决,即它重新连接并尝试将错误的蒸汽再次发送到Azure并且它可以工作,然后它会触发“完成”。事件,我们得到Callback already called

我是否正确处理了流事件中的回调?

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

enter image description here

1 个答案:

答案 0 :(得分:4)

正如您所知,您正在让您的回叫被调用两次。问题是;你想在迭代流时停止所有错误,还是想从流中累积所有错误?

有多种方法可以捕获和处理您正在执行的错误,但是因为您没有抛出错误对象导致从数据流中发生额外调用而导致致命错误。

您的代码中的实际问题是由于您的退货范围。当您处理错误并尝试返回回调并暂停脚本执行时,小时返回的范围是流错误处理程序的本地,而不是全局脚本,因此脚本继续并捕获继续下一个有效的流。

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(原始node.js语言支持者)提供的错误处理帮助,但在处理错误时应该会让您对选项有所了解。

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