Как правильно вызвать список асинхронных функций по порядку?
Я пытаюсь написать подобную "robocopy /mir" функцию в Node.js и не могу понять, как правильно выполнить несколько асинхронных функций по порядку.
Немного предыстории:
- Скрипт запускается в Windows, поэтому мне нужно было найти способ копировать файлы, сохраняя время модификации и получая уведомления о прогрессе.
- Чтобы решить эту проблему, я пошел дальше и написал свою функцию копирования в.NET (вызывая ее с помощью Edge.js)- эта функция копирования просто вызывает функцию Node, сообщающую о ходе копирования файла. Эта часть работает без нареканий.
Для того, чтобы файлы копировались по порядку, моей первой мыслью было сделать что-то вроде следующего:
Object.keys(filesToCopy).forEach(function(key) {
var deferred = q.defer();
var payload = {
sourcePath: key,
destPath: filesToCopy[key],
progressCallback: progressCallback
};
console.log('Copying %s...', sourcePath);
// Edge.js called here
copyFile(payload, deferred.makeNodeResolver());
deferred.promise.then(function(result) {
console.log('%s complete.', result);
}, function(err) {
console.error('Error: ', err.message);
});
promises.push(deferred.promise);
});
К сожалению, это (как и ожидалось) начинает копировать каждый файл, как только вызывается функция.NET, поэтому я получаю уведомления о прогрессе для всех файлов сразу, давая мне вывод, например:
1%
2%
1%
2%
3%
3%
Похоже, мне нужен способ поставить в очередь работу, которую нужно выполнить, прежде чем запускать ее сразу, причем каждый элемент завершается до того, как будет продолжен следующий. Когда все пункты будут завершены, я должен быть уведомлен. Решение кажется достаточно простым, но оно по-прежнему ускользает от меня, так как все, что я пытаюсь сделать, связаны с другой проблемой. Любая помощь будет принята с благодарностью, спасибо!
РЕДАКТИРОВАТЬ: Как указано в моем комментарии, ответ Берги использовал функцию, которая фактически вернула обещание, в то время как моя функция Edge.js не сделала. Я смог решить мою проблему сначала, используя массив вместо объекта для filesToCopy
затем делать что-то вроде этого:
return filesToCopy.reduce(function(prev, curr) {
return prev.then(function() {
var deferred = q.defer();
copyFile(curr, function(err, result) {
deferred.resolve(result);
console.log('Completed %s', result);
});
return deferred.promise;
})
}, q());
Возможно, это не лучший способ сделать это, но он подходит для моего использования.
2 ответа
Может быть, что-то подобное поможет?
var $j = function(val, space) {
return JSON.stringify(val, null, space || '')
}
var log = function(val) {
document.body.insertAdjacentHTML('beforeend', '<div><pre>' + val + '</div></pre>')
}
var files = '12345'.split('').map(function(v) {
return {
name: 'file_' + v + '.js',
load: function() {
var cur = this;
var pro = new Promise(function(resolve, reject) {
log('loading : ' + cur.name);
// we simualate the loading stuff
setTimeout(function() {
resolve(cur.name);
}, 1 * 1000);
}).then( function( val ) {
// once loaded
log('loaded : ' + val);
return val;
});
return pro;
}
};
});
files.reduce(function(t, v) {
t.promise = t.promise.then(function(){
return v.load();
});
return t;
}, {
promise: Promise.resolve(1)
});
Использование async.eachSeries
на массивах или async.forEachOfSeries
на объектах.
Зацикливание объекта
var async = require('async');
var filesObject = {'file/path/1': {}, 'file/path/2': {}};
async.forEachOfSeries(filesObject, copyFileFromObj, allDone);
function copyFileFromObj(value, key, callback) {
console.log('Copying file ' + key + '...');
callback(); // when done
}
function allDone(err) {
if (err) {
console.error(err.message);
}
console.log('All done.');
}
Зацикливание массива
var async = require('async');
var filesArray = ['file/path/1', 'file/path/2'];
async.eachSeries(filesArray, copyFile, allDone);
function copyFile(file, callback) {
console.log('Copying file ' + file + '...');
callback(); // when done
}
function allDone(err) {
if (err) {
console.error(err.message);
}
console.log('All done.');
}
Рабочий пример здесь: https://tonicdev.com/edinella/sync-loop-of-async-operations