Ответ 1
Найдите Шаговый модуль. Он может связывать вызовы асинхронных функций и передавать результаты от одного к другому.
Я пытаюсь обернуть голову вокруг этой проблемы в течение последних часов, но не могу понять это. Думаю, мне все равно придется привыкнуть к стилю функционального программирования;)
Я написал рекурсивную функцию, которая проходит через структуру каталогов и делает вещи определенным файлам. Эти функции используют асинхронные методы ввода-вывода. Теперь я хочу выполнить какое-то действие, когда все это происходит.
Как я могу убедиться, что это действие выполняется после того, как все вызовы parse
были выполнены, но все еще используют асинхронные функции ввода-вывода?
var fs = require('fs'),
path = require('path');
function parse(dir) {
fs.readdir(dir, function (err, files) {
if (err) {
console.error(err);
} else {
// f = filename, p = path
var each = function (f, p) {
return function (err, stats) {
if (err) {
console.error(err);
} else {
if (stats.isDirectory()) {
parse(p);
} else if (stats.isFile()) {
// do some stuff
}
}
};
};
var i;
for (i = 0; i < files.length; i++) {
var f = files[i];
var p = path.join(dir, f);
fs.stat(p, each(f, p));
}
}
});
}
parse('.');
// do some stuff here when async parse completely finished
Найдите Шаговый модуль. Он может связывать вызовы асинхронных функций и передавать результаты от одного к другому.
Вы можете использовать асинхронный модуль. Его функция авто является удивительной. Если у вас есть функция A() и функция B() и функция C(). Обе функции B() и C() зависят от функции A(), которая использует возврат значения из функции A(). используя функцию асинхронного модуля, вы можете убедиться, что функции B и C будут выполняться только при завершении выполнения функции.
Ссылка: https://github.com/caolan/async
async.auto({
A: functionA(){//code here },
B: ['A',functionB(){//code here }],
C: ['A',functionC(){//code here }],
D: [ 'B','C',functionD(){//code here }]
}, function (err, results) {
//results is an array that contains the results of all the function defined and executed by async module
// if there is an error executing any of the function defined in the async then error will be sent to err and as soon as err will be produced execution of other function will be terminated
}
})
});
В приведенном выше примере функция B и functionC будут выполняться вместе, как только функция Выполнение будет завершено. Таким образом, функция B и functionC будут выполняться одновременно
functionB: ['A',functionB(){//code here }]
В приведенной выше строке мы передаем значение return функцией functionA, используя 'A'
и функция D будет выполняться только после завершения выполнения функции B и functionC.
если в любой функции будет ошибка, тогда выполнение другой функции будет завершено, и будет выполняться функция ниже. Где вы могли бы написать свою логику успеха и сбоя.
function (err, results) {}
При успешном выполнении всех функций "результаты" будут содержать результат всех функций, определенных в async.auto
function (err, results) {}
Взгляните на модификацию исходного кода, который делает то, что вы хотите, без асинхронных вспомогательных библиотек.
var fs = require('fs'),
path = require('path');
function do_stuff(name, cb)
{
console.log(name);
cb();
}
function parse(dir, cb) {
fs.readdir(dir, function (err, files) {
if (err) {
cb(err);
} else {
// cb_n creates a closure
// which counts its invocations and calls callback on nth
var n = files.length;
var cb_n = function(callback)
{
return function() {
--n || callback();
}
}
// inside 'each' we have exactly n cb_n(cb) calls
// when all files and dirs on current level are proccessed,
// parent cb is called
// f = filename, p = path
var each = function (f, p) {
return function (err, stats) {
if (err) {
cb(err);
} else {
if (stats.isDirectory()) {
parse(p, cb_n(cb));
} else if (stats.isFile()) {
do_stuff(p+f, cb_n(cb));
// if do_stuff does not have async
// calls inself it might be easier
// to replace line above with
// do_stuff(p+f); cb_n(cb)();
}
}
};
};
var i;
for (i = 0; i < files.length; i++) {
var f = files[i];
var p = path.join(dir, f);
fs.stat(p, each(f, p));
}
}
});
}
parse('.', function()
{
// do some stuff here when async parse completely finished
console.log('done!!!');
});
Что-то вроде этого будет работать - базовое изменение вашего кода - это цикл, превращенный в рекурсивный вызов, который расходует список, пока он не будет выполнен. Это позволяет добавить внешний обратный вызов (где вы можете выполнить некоторую обработку после завершения синтаксического анализа).
var fs = require('fs'),
path = require('path');
function parse(dir, cb) {
fs.readdir(dir, function (err, files) {
if (err)
cb(err);
else
handleFiles(dir, files, cb);
});
}
function handleFiles(dir, files, cb){
var file = files.shift();
if (file){
var p = path.join(dir, file);
fs.stat(p, function(err, stats){
if (err)
cb(err);
else{
if (stats.isDirectory())
parse(p, function(err){
if (err)
cb(err);
else
handleFiles(dir, files, cb);
});
else if (stats.isFile()){
console.log(p);
handleFiles(dir, files, cb);
}
}
})
} else {
cb();
}
}
parse('.', function(err){
if (err)
console.error(err);
else {
console.log('do something else');
}
});
См. следующее решение, оно использует deferred модуль:
var fs = require('fs')
, join = require('path').join
, promisify = require('deferred').promisify
, readdir = promisify(fs.readdir), stat = promisify(fs.stat);
function parse (dir) {
return readdir(dir).map(function (f) {
return stat(join(dir, f))(function (stats) {
if (stats.isDirectory()) {
return parse(dir);
} else {
// do some stuff
}
});
});
};
parse('.').done(function (result) {
// do some stuff here when async parse completely finished
});
Я с большим успехом использовал syncrhonize.js. Там даже ожидающий запрос на подачу (который работает достаточно хорошо) для поддержки асинхронных функций, которые имеют несколько параметров. Гораздо лучше и проще в использовании, чем node -sync imho. Добавлен бонус, что у него есть простая в понимании и тщательная документация, тогда как node -sync нет.
Поддерживает два разных метода для подключения синхронизации, отложенной/ожидающей модели (например, что предлагал @Mariusz Nowak) и более тонкий, хотя и не-гранулированный подход к функции-цели. Документы для каждого из них довольно понятны.
Рекомендовать использовать node -seq https://github.com/substack/node-seq
установлен с номером npm.
Я использую его, и мне это нравится.
Ищите node -sync, простую библиотеку, которая позволяет вам синхронно вызывать любую асинхронную функцию. Главное преимущество заключается в том, что он использует javascript-native design - Function.prototype.sync, вместо тяжелых API, которые вам нужно будет изучить. Кроме того, асинхронная функция, которая была вызвана синхронно через node -sync, не блокирует весь процесс - он блокирует только текущий поток!