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

Предписывающий Node.js

Я начал играть с Node.js в последнее время, и я столкнулся с ситуацией, когда мне нужно небольшое руководство по предписывающему пути Node.js для выполнения задачи. В этом конкретном случае мне нужно создать группу каталогов, и, когда все каталоги были созданы, мне нужно выполнить некоторую окончательную операцию. Порядок, в котором создаются каталоги, не имеет значения, мне просто нужно выполнить окончательную операцию после последнего.

Самый простой способ добиться этого - вернуться к старым синхронным привычкам. То есть просто вызовите fs.mkdirSync для каждого из каталогов и выполните операцию в конце. Например:

fs.mkdirSync('a', 0755);
fs.mkdirSync('a/b', 0755);
fs.mkdirSync('a/b/c', 0755);
performFinalOperation();

Пока это сработает, ему не кажется, что это способ Node.js. Очевидно, что программа блокируется, пока она ждет, когда ОС создаст каталог и вернется. В сильно загруженной системе с файловой системой, смонтированной удаленно, каждый из вызовов mkdirSync может занять очень много времени. Таким образом, это не лучший подход.

Одним из основных пунктов продажи Node.js является тот факт, что он асинхронный. Таким образом, вызовы fs.mkdir могут быть скованы через обратные вызовы:

fs.mkdir('a', 0755, function(e) {
    if (!e) {
        fs.mkdir('a/b', 0755, function(e) {
            if (!e) {
                fs.mkdir('a/b/c', 0755, function(e) {
                    if (!e) {
                        performFinalOperation();
                    }
                });
            }
        });
    }
});

Опять же, этот подход, я уверен, работает, но это приводит к действительно глубокому вложению и дублированию кода. У него есть преимущество не блокировать, пока каталоги создаются, но по какой цене?

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

(function (directories) {
    if (directories.length === 0) {
        performFinalOperation();
    } else {
        var tail = arguments.callee;
        fs.mkdir(directories.shift(), 0755, function(e) {
            tail(directories);
        });
    }
})(['a', 'a/b', 'a/b/c']);

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

Итак, хотя конкретный вопрос заключается в создании каталогов, меня больше интересует подход, который прибегает опытный ветеран Node.js, когда возникает такая ситуация. Мне особенно неинтересно, какие библиотеки существуют, чтобы сделать это проще.

4b9b3361

Ответ 1

О, эй Брайан:)

Я специально не интересуюсь какие библиотеки существуют, чтобы сделать это проще.

Прикованный ветеран Node написал, по крайней мере, один из своих собственных библиотек потока управления. Мы просто скопировали классы Twisted Deferred, так как они уже усердно работали и изучали асинхронное программирование. Это инвертирует стандартный шаблон обратного вызова как аргумента, и мне нравится результирующий код, но если вы хотите вложить кучу обратных вызовов, вы все равно можете сделать это с помощью Deferred и в итоге получите столько же беспорядка.

С ограничением использования библиотек люди обычно делают именно то, что вы написали. На самом деле нет другого выбора. Без языковых изменений, таких как генераторы, мы можем лучше всего использовать библиотеки. Если вы не хотите использовать существующий, вы в конечном итоге сворачиваете свои собственные или просто пишете много шаблонов.

Ответ 2

Ваше второе решение может быть значительно упрощено и включать такие ошибки:

var mkdirs = function(dirs, mode, cb){
  (function next(e) {
    (!e && dirs.length) ? fs.mkdir(dirs.shift(), mode, next) : cb(e);
  })(null);
};

Ответ 3

npm install mkdirp

var mkdirp = require('mkdirp').mkdirp;

mkdirp('/tmp/foo/bar/baz', 0755, function (err) {
    if (err) console.error(err)
    else console.log('pow!')
});

Ответ 4

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

См. http://howtonode.org/control-flow-part-iii

Ответ 5

Hm, это спекуляция, и я почти не знаю Javascript, но как насчет чего-то с таким конечным результатом?

Asynchronously
  .Do(function(callback) { 
    fs.mkdir('a', 0755, callback);
  })
  .Then(function(result, callback) { 
    if(!result)
      fs.mkdir('a/b', 0755, callback); 
  })
  .Then(function(result, callback) {
    if(!result)
      fs.mkdir('a/b/c', 0755, callback);
  })
  .Then(function(result, callback) {
    if(!result)
      performFinalOperation();
  });

Ответ 6

  • Опытные ветераны писали библиотеки, чтобы упростить это. Вы не хотите слышать об этом, поэтому я не собираюсь вдаваться в подробности, но мне действительно нравится async.js.
  • Также этот пост (видео) из Yahoo! действительно интересно избегать написания спагетти-кода

Ответ 7

Я бы рекомендовал посмотреть Step. Это упрощает переход от синхронного к асинхронному.

Ответ 8

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

var fs = require('fs');

function mkdirs(dirs, cb, err) {
    if (err) {
        return cb(err);
    }

    if (dirs.length === 0) {
        return cb();
    }

    var dir = dirs.shift();
    fs.mkdir(dir, mkdirs.bind(this, dirs, cb));
}

// Test it.
mkdirs(['a', 'a/b', 'a/b/c'], function (e) {
    if (e) {
        return console.log("An error:", e);
    }

    console.log("No error.");
});