Выполнение асинхронных вызовов синхронно - PullRequest
30 голосов
/ 10 мая 2011

Я пытался обдумать эту проблему последние часы, но не могу понять.Я думаю, мне все еще нужно привыкнуть к функциональному стилю программирования;)

Я написал рекурсивную функцию, которая проходит через структуру каталогов и выполняет определенные действия с определенными файлами.Эта функция использует асинхронные методы ввода-вывода.Теперь я хочу выполнить какое-то действие после завершения всего этого обхода.

Как мне убедиться, что это действие выполнено после того, как все вызовы 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

Ответы [ 8 ]

12 голосов
/ 10 мая 2011

Ищите Шаг модуля .Он может связывать вызовы асинхронных функций и передавать результаты от одного к другому.

4 голосов
/ 16 декабря 2014

Вы можете использовать асинхронный модуль. Его автоматическая функция потрясающая. Если у вас есть функция A () и функция B () и функция C (). Обе функции B () и C () зависят от функции A (), которая использует возвращаемое значение из функции A (). используя функцию асинхронного модуля, вы можете быть уверены, что функции B и C будут выполняться только после завершения выполнения функции A.

Ссылка: 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
            }
        })
    });

В приведенном выше примере functionB и functionC будут выполняться вместе, как только выполнение функции A будет завершено. Таким образом, functionB и functionC будут выполняться одновременно

functionB: ['A',functionB(){//code here }]

В приведенной выше строке мы передаем значение, возвращаемое функцией A, используя 'A'

и functionD будут выполняться только тогда, когда будет завершено выполнение functionB и functionC.

если в какой-либо функции возникнет ошибка, то выполнение другой функции будет прекращено и будет выполнена функция ниже. Где вы могли бы написать свою логику успеха и неудачи.

function (err, results) {}

При успешном выполнении всех функций «results» будет содержать результат всех функций, определенных в async.auto

function (err, results) {}
3 голосов
/ 11 мая 2011

Взгляните на модификацию вашего исходного кода, которая делает то, что вы хотите без асинхронных вспомогательных библиотек.

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!!!');
});
1 голос
/ 19 января 2014

Я пользуюсь syncrhonize.js с большим успехом. Существует даже ожидающий запрос на включение (который работает достаточно хорошо) для поддержки асинхронных функций, которые имеют несколько параметров. Гораздо лучше и проще в использовании, чем синхронизация узлов imho. Дополнительным бонусом является то, что он имеет простое для понимания и подробное документирование, тогда как синхронизация узлов - нет.

Поддерживает два различных метода подключения синхронизации: модель отложенного / ожидаемого (например, то, что предлагал @Mariusz Nowak) и более тонкий, но не гранулярный подход к функции-цели. Документы довольно просты для каждого.

1 голос
/ 08 июля 2011

См. Следующее решение, используется модуль 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
});
1 голос
/ 10 мая 2011

Примерно так будет работать - основное изменение в вашем коде - это превращение цикла в рекурсивный вызов, который использует список до тех пор, пока это не будет сделано.Это позволяет добавить внешний обратный вызов (где вы можете выполнить некоторую обработку после завершения анализа).

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');
  }
});
0 голосов
/ 08 октября 2012

Ищите node-sync , простую библиотеку, которая позволяет вам вызывать любую асинхронную функцию синхронно.Основное преимущество заключается в том, что он использует собственный дизайн javascript - функцию Function.prototype.sync вместо тяжелых API, которые вам необходимо изучить.Кроме того, асинхронная функция, которая была вызвана синхронно через синхронизацию узлов, не блокирует весь процесс - она ​​блокирует только текущий поток!

0 голосов
/ 11 мая 2011

Рекомендую использовать node-seq https://github.com/substack/node-seq

установлен npm.

Я использую это, и мне это нравится ..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...