Подтвердить что ты не робот

Выполнение асинхронных вызовов синхронно

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

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

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

Ответ 1

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

Ответ 2

Вы можете использовать асинхронный модуль. Его функция авто является удивительной. Если у вас есть функция 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) {}

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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