Node.JS — Основы асинхронного программирования, часть 1

    Сейчас, после выхода стабильной версии Node.JS 0.2.0, я решил начать цикл статей по программированию с его использованием.

    Основная концепция Node.JS — любые операции ввода-вывода по умолчанию реализованы как асинхронные, после выполнения операции будет вызвана функция обратного вызова, первым параметром которой будет являться ошибка или null.

    Скрываем асинхронную вложенность


    Предположим, нам нужно создать каталог, включая всех его родителей. И только в случае, если его удалось создать, начать писать в этот каталог.

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

    var path     = require('path');
    var fs      = require('fs');

    var mkdir_p = function(pth, callback)
    {
        fs.stat(pth, function(err, stat)
        {
            if (!err && stat)
            {
                callback(null);
                return;
            }
            mkdir_p(path.dirname(pth), function(err)
            {
                if (err)
                {
                    callback(err);
                    return;
                }
                fs.mkdir(pth, 0755, callback);
            });
        });
    };

    * This source code was highlighted with Source Code Highlighter.

    Функция сперва проверяет наличие каталога, и в случае его наличия вызывает функцию обратного вызова без ошибки. В случае, если каталог отсутствует, вызывается mkdir_p для каталога-родителя, и затем создаётся искомый каталог.

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

    Используем, например, так:

      mkdir_p(path.dirname(outPath), function(err)
      {
        if (err)
        {
          response.writeHead(500, {'Content-Type': 'text/plain'});
          response.end('Cannot create directory');
          return;
        }
        getFile(url, inPath, function(err)
        {
          if (err)
          {
            response.writeHead(500, {'Content-Type': 'text/plain'});
            response.end('Cannot read file');
            return;
          }
          transcode(inPath, outPath, function(err)
          {
            if (err)
            {
              response.writeHead(500, {'Content-Type': 'text/plain'});
              response.end('Cannot transcode file');
              return;
            }
            sendRemoveFile(outPath, response, function(err)
            {
              if (err)
              {
                console.log('Cannot send file');
                return;
              }
            });
          });
        });
      });

    * This source code was highlighted with Source Code Highlighter.


    В следующей статье расскажу вам о работе с внешними процессами.

    Поделиться публикацией

    Комментарии 17

      +10
      Как-то сумбурно очень. Мне кажется, что те кто хочет начать изучать асинхронное программирование, надо начинать с того, что это, чем отличается. А для тех кто в теме, тому вряд ли этот конкретный (не очень разобранный) пример принесет пользу.
        –5
        Сперва я хотел рассмотреть уже готовое приложение, но понял, что нужно разбить его на отдельные куски.

        Собственно, хочется показать в результате нечто весьма полезное. И это будет сделано, полагаю, уже в следующей статье.
          +3
          если готовое приложение будет рассматриваться — то стоит в начале описать к чему хотим прийти в конце статей, а то выдраный кусок не очень понятен по смыслу :)
            –5
            Мне казалось, что скелет приложения говорит сам за себя :)

            В данном случае это транскодер видеофайлов.

            Хотя раньше я покажу, думаю, генерацию CSS-спрайтов.
              +5
              Если честно, я прочитав статью, даже не понял, что это будет целое приложение.
              И скелета я тоже не заметил ))
              А уж а транскодере видеофайлов я даже подумать не мог )
                –2
                mkdir_p -> getFile -> transcode -> sendRemoveFile ?)
                  0
                  да прошу прощения, но на названия я не очень обратил внимание, вникая в суть самого примера. Все таки вставить вступительную речь, о том что рассказано будет на примере приложения такого и вот первая задача из него мне кажется стоит.
        +11
        обрадовался, прочитал, расстроился
          +2
          Угу, прочитав название и первое предложение, подумал, что хоть несколько слов будет сказано о том, что такое Node.js, какие задачи решает и чем лучше/хуже конкурентов, ну хоть ссылку на такую статью и прмеры простейших приложений
            +1
            Имхо, об этом уже много написано, в том числе и на Хабре.
          0
          как то грустно получилось. Надеюсь самая интересная часть транскодера будет описана более радужно и интересно. Для новичка в node.js этот код как азбука младенцу
            0
            Ай-ай-ай, в Node.JS же другой code style принят? ;)
              0
              По сути дела: я бы в своё время обратил внимание читателей на nextTick, им удобно разбивать подобные операции на кусочки, выполняющиеся с промежутками в event-loop. Спасёт в случае, если каждый шаг рекурсии долго выполняется и не даст переполнить стек вызовов.
                0
                До этого ещё несколько статей, если интерес будет )

                Чуть позже напишу о работе с двумя потоками асинхронных операций одновременно, а затем первое реальное приложение.
              0
              А вы всегда пишете Javascript-код в C++ стиле?
                0
                Зависит от проекта.

                Если проект ни от кого не зависит, пишу в том стиле, который мне больше нравится (вернее, к которому больше привык).
                0
                Гм, асинхронное программирование != использование замыканий.

                Путаетесь сами — путаете народ.
                Ваше приложение будет выполнятся четко синхронно. Последовательно и единобразно.
                Вообще его можно представитить как три очереди — скандиректорий — скан файлов — этот-хренов-транскодер.
                Параллельность параллельных задач != асинхроность :)

                Давайте лучше запустим сканирование директорий, после чего отработаем некий шаблон «шапки страницы», после чего встанем в слип в ожидании готовности данных и сгенерим выходной поток реально в несколько параллельных задач. И еще в транскодере сделаем проверку на колличество одновременно запущеных инстансов. А то когда их много — будут тупить…

                В общем подводя итоги — эффекты паралельности можно добиться
                1.системой слотой и сигналов(ивентов в общем)
                2.очередями, в том числе множественными. Вообще это называется конвеер
                3.простым отсуствием обратной связи у вложености скриптов.
                В смысле (в данном случае) так как функция transcode не вызывает никаких телодвижений в функциях ее породивших, как и другие функции примера… это получается конвеер обыкновенный, точнее не асинхронное программирование, а просто распределеное.

                Какой пример асинхронности можно привести в каноническом виде?
                Win32
                ReadFileEx(асихронное чтение) + readcallback(асихнронный колбэк у этого чтения) + WaitForSingleObject(или QueueUserAPC на вызывающий тред) в главном треде.

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое