NodeJS и асинхронный ад - PullRequest
       11

NodeJS и асинхронный ад

7 голосов
/ 05 июня 2011

Я только что попал в эту ужасную ситуацию, когда у меня есть массив строк, каждая из которых представляет возможно существующий файл (например, var files = ['file1', 'file2', 'file3']. Мне нужно пройтись по этим именам файлов и попытаться выяснить, существует ли он в текущем каталоге, иесли это произойдет, прекратите зацикливание и забудьте остальные оставшиеся файлы. Поэтому в основном я хочу найти первый из существующих файлов и вернуться к жестко запрограммированному сообщению, если ничего не найдено.

Вот чтоВ настоящее время у меня есть:

var found = false;
files.forEach(function(file) {
  if (found) return false;

  fs.readFileSync(path + file, function(err, data) {
    if (err) return;

    found = true;
    continueWithStuff();
  });
});

if (found === false) {
  // Handle this scenario.
}

Это плохо. Он блокирует (readFileSync), поэтому он медленный.

Я не могу просто предоставить методы обратного вызова для fs.readFile, это не так просто, потому чтоМне нужно взять первый найденный элемент ... и обратные вызовы могут быть вызваны в любом случайном порядке. Я думаю, что одним из способов может быть обратный вызов, который увеличивает счетчик и хранит список найденной / не найденной информации, и когда он достигаетсчет files.length, затем он проверяет найденную / не найденную информацию и решает, что делать дальше.

Это больно. Я вижу результатЧеловеческое величие в вечернем IO, но это недопустимо.Какой выбор у меня есть?

Ответы [ 5 ]

14 голосов
/ 05 июня 2011

Не используйте вещи синхронизации в обычной серверной среде - вещи однопоточные, и это полностью заблокирует их, пока он ожидает результатов этого связанного цикла io.Утилита CLI = вероятно, хорошо, сервер = только хорошо при запуске.

Общая библиотека для асинхронного управления потоком: https://github.com/caolan/async

async.filter(['file1','file2','file3'], path.exists, function(results){
    // results now equals an array of the existing files
});

И если вы хотите сказать, избегайте дополнительных вызововк path.exists, тогда вы могли бы довольно легко написать функцию 'first', которая выполняла операции, пока какой-либо тест не был выполнен успешно.Похоже на https://github.com/caolan/async#until - но вы заинтересованы в выводе.

2 голосов
/ 08 июня 2011

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

2 голосов
/ 07 июня 2011

Асинхронная библиотека - это абсолютно то, что вы ищете. Он предоставляет практически все типы итераций, которые вы бы хотели, в хорошем асинхронном режиме. Вам не нужно писать свою собственную «первую» функцию. Async уже предоставляет функцию some, которая делает именно это.

https://github.com/caolan/async#some

async.some(files, path.exists, function(result) {
  if (result) {
    continueWithStuff();
  }
  else {
    // Handle this scenario
  }
});

Если вы или кто-либо, читающий это в будущем, не захотите использовать Async, вы также можете сделать свою собственную базовую версию 'some'.

function some(arr, func, cb) {
  var count = arr.length-1;

  (function loop() {
    if (count == -1) {
      return cb(false);
    }
    func(arr[count--], function(result) {
      if (result) cb(true);
      else loop();
    });
  })();
}

some(files, path.exists, function(found) {
  if (found) { 
    continueWithStuff();   
  }
  else {
    // Handle this scenario
  }
});
0 голосов
/ 11 февраля 2016

Используйте async.waterfall для управления асинхронным вызовом в node.js, например: включив async-library и использовать вызов водопада в асинхронном режиме:

 var async = require('async'); 
 async.waterfall( 
   [function(callback) 
     { 
       callback(null, taskFirst(rootRequest,rootRequestFrom,rootRequestTo, callback, res));
     },
     function(arg1, callback) 
     {
       if(arg1!==undefined )
       {
         callback(null, taskSecond(arg1,rootRequest,rootRequestFrom,rootRequestTo,callback, res));  
       }      
     }
   ])
0 голосов
/ 05 июня 2011

(Правка: удалено предложение синхронизации, потому что это не очень хорошая идея, и мы бы не хотели, чтобы кто-то копировал / вставлял и использовал его в рабочем коде, не так ли?)

Если вы настаиваете на использовании асинхронных программ, я думаю, что простой способ реализовать это, чем то, что вы описали, заключается в следующем:

var path = require('path'), fileCounter = 0;

function existCB(fileExists) {
    if (fileExists) {
        global.fileExists = fileCounter;
        continueWithStuff();
        return;
    }
    fileCounter++;
    if (fileCounter >= files.length) {
        // none of the files exist, handle stuff
        return;
    }
    path.exists(files[fileCounter], existCB);
}

path.exists(files[0], existCB);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...