@rodionov12

Как в Node.JS правильно использовать callback?

Пишу асинхронный загрузчик картинок, потребовалось считать хэш файла после загрузки. Не могу реализовать

Вот код программы:
var images = {};
        for (var i in news.items) {
            if(typeof news.items[i].attachments !== undefined) {
                for (var j in news.items[i].attachments)
                    if (news.items[i].attachments[j].type == 'photo') {
                        download(news.items[i].attachments[j].photo.photo_604, 'tmp/' + url.parse(news.items[i].attachments[j].photo.photo_604).pathname.split('/').pop(), function() {
                            fs.readFile('tmp/' + url.parse(news.items[i].attachments[j].photo.photo_604).pathname.split('/').pop(), function(err, data) {
                                console.log(checksum(data, 'sha1'));
                            });
                        });
                    }
            }
        }


Вот код функций download и checksum
var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  https.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);  // close() is async, call cb after close completes.
    });
  });
};

function checksum (str, algorithm, encoding) {
    return crypto
        .createHash(algorithm || 'md5')
        .update(str, 'utf8')
        .digest(encoding || 'hex')
}


В консоли выводятся рандомные хэши, а если попытаться вывести i или j - они постоянные и не меняются. Как исправить эту проблему?
  • Вопрос задан
  • 282 просмотра
Решения вопроса 1
Если вы хотите оставить эту прелестную вложенность в коде, то можно так

var images = {};
        for (var i in news.items) {
            if(typeof news.items[i].attachments !== undefined) {
                for (var j in news.items[i].attachments)
                    if (news.items[i].attachments[j].type == 'photo') {
                        download(news.items[i].attachments[j].photo.photo_604, 'tmp/' + url.parse(news.items[i].attachments[j].photo.photo_604).pathname.split('/').pop(), function(i, j) {
                            fs.readFile('tmp/' + url.parse(news.items[i].attachments[j].photo.photo_604).pathname.split('/').pop(), function(err, data) {
                                console.log(checksum(data, 'sha1'));
                            });
                        }.bind(i, j));
                    }
            }
        }


Или так:
var images = {};
Object.keys(news.items).forEach(function(key) {
    var item = news.items[key];
    if (!Array.isArray(item.attachments)) return;

    item.attachments.forEach(function(attachment) {
        it (attachment.type !== 'photo') return;

        var loc_path = 'tmp/' + url.parse(attachment.photo.photo_604).pathname.split('/').pop();
        download(attachment.photo.photo_604, loc_path, function() {
            fs.readFile(loc_path, function(data) {
                console.log(checksum(data, 'sha1'));
            });
        });
    });
});


Это минимум, который нужен чтобы код стал читаемым.
Дальше я не могу понять зачем вы сначала скачиваете файл в файловую систему, и потом грузите его в память, почему бы сразу не грузить в память.
Так же рекомендую использовать промисы вместо каллбеков - это сделает код еще более читабельным.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
Все дело в асинхронности. Когда коллбэк выполнится (следующий тик ивент-лупа), цикл уже закончится и i и j достигнут последнего значения.

Тебе нужны промисы.
Ответ написан
Комментировать
ACCNCC
@ACCNCC
Делаю игры!
Не забываем про асинхронность и используем https://www.npmjs.com/package/bluebird
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы