Как запустить код с помощью Node.js Fibers

У меня есть вопрос о Nodejs Fibers(который для меня абсолютно новый) ... У меня есть этот учебник для Nodejs Fibers, http://bjouhier.wordpress.com/2012/03/11/fibers-and-threads-in-node-js-what-for/, и здесь был пример

    var fiber = Fiber.current;
    db.connect(function(err, conn) {
    if (err) return fiber.throwInto(err);
       fiber.run(conn);
    });
   // Next line will yield until fiber.throwInto 
   // or fiber.run are called
   var c = Fiber.yield();
   // If fiber.throwInto was called we don't reach this point 
   // because the previous line throws.
   // So we only get here if fiber.run was called and then 
   // c receives the conn value.
   doSomething(c);
   // Problem solved! 

Теперь на основе этого примера я создал свою собственную версию кода, как этот,

  var Fiber = require('fibers');

  function sample(callback){
     callback("this callback");
  }

  var fiber = Fiber.current;
  sample(function(string){
     fiber.run(string);
  });
  var string = Fiber.yield();
  console.log(string);

но это дает мне ошибку,

/home/ubuntu/Tasks/ServerFilteringV1/test.js:28
    fiber.run(string);
      ^
TypeError: Cannot call method 'run' of undefined

И у меня есть еще один случай, который будет запускать функцию через 1000 мс с обратным вызовом внутри (я сделал это для тестирования функций с длительным выполнением перед обратным вызовом),

var Fiber = require('fibers');

function forEach(callback){
   setTimeout(function(){
       callback("this callback");
   },1000);
}


var fiber = Fiber.current;
forEach(function(string){
   fiber.run(string);
});
var string = Fiber.yield();
console.log(string);

Этот код здесь дает мне еще одну ошибку,

/home/ubuntu/Tasks/ServerFilteringV1/test.js:30
var string = Fiber.yield();
                    ^
Error: yield() called with no fiber running

Хорошо, должен ли yield() ждать после выполнения функции run()? Любая идея о том, что происходит в моем коде nodejs? И спасибо заранее...

2 ответа

Решение

Пример 1

Волокно - это своего рода легкая нить исполнения. Подобно реальным потокам и процессам, волокну должен быть предоставлен блок кода для выполнения при запуске. Код, который вы взяли у bjouhier, не работает как есть. Он был предназначен для работы внутри волокна, вот так:

var f = Fiber(function() {
    var fiber = Fiber.current;

    sample(function(str) {
        fiber.run(string);
    });

    var str = Fiber.yield();
    console.log(str);
});

f.run();

призвание run на волокне, ну, работает код волокна, который был передан как обратный вызов Fiber, Однако приведенный выше код также выдаст ошибку (указав, что оптоволокно уже запущено). Можно легко понять, почему при анализе порядка исполнения.

  1. Установить переменную f как волокно.
  2. Запустите волокно:
    1. Установить переменную fiber указывая на текущее бегущее волокно.
    2. Функциявызова sample,
    3. Перезвоните.
    4. Вызов fiber.run, что дает ошибку, так как текущее волокно уже работает.

Структура этого кода правильная, но предполагает sample некоторая асинхронная функция, которая не вызывает немедленно обратный вызов Давайте поменяем ваш sample функция по этому:

function sample(callback) {
    setTimeout(function() {
        callback("this callback");
    }, 500);
}

Теперь вышеприведенный код не выдаст ошибку, так как sample немедленно возвращается. Порядок исполнения внутри волокна:

  1. Задавать fiber указывая на текущее бегущее волокно.
  2. Вызов sample, который возвращает без вызова обратного вызова (пока).
  3. Вызовите Fiber.yield(), который "приостанавливает" текущее волокно.
  4. Через 500 мс позвоните в систему обратного вызова.
  5. Вызов fiber.run() прохождение "этот обратный вызов", который возобновляет волокна.
  6. Fiber.yield возвращает, установите str в "этот обратный вызов".
  7. Записать строку в консоль.

Обратите внимание, что шаг 4 выполняется вне выполнения волокна.

Пример 2

В то время как в первом примере не было бегущего волокна (и, следовательно, fiber был неопределен), во втором примере ошибка выдается по той же причине. Опять же, код должен работать внутри волокна.


Функция урожайности и запуска

Волокно должно совместно управлять другим волокном (или основной линией исполнения). Сравните это с преимущественным характером потоков и процессов. Отказ от контроля - это то, что подразумевается под "уступкой контроля", и в этом случае это делается Fiber.yield(),

Чтобы продолжить выполнение (непосредственно после точки, где получено волокно), необходимо вызвать run() на волокне.

Механизм передачи значений в и из волокна заключается во взаимодействии yield и run:

  • Аргумент, данный run (который находится за пределами волокна), возвращается yield (внутри волокна).
  • Аргумент, данный yield (внутри волокна), возвращается run (вне волокна).

Для примера посмотрите на инкрементный генератор в репозитории github узловых волокон. Кроме того, обратите внимание, что наш Пример 1, обратный вызов, данный sample по существу, запускается за пределы волокна, так как запускается на следующем тике (а именно: асинхронная природа setTimeout).

Как объяснил Эндрю, и как подсказано в моем блоге (см. Предложение, которое следует за примером), вы должны создать Fiber и запустить его с run() уметь звонить Fiber.yield,

Преимущества волокон не очевидны, когда у вас есть один асинхронный вызов, но рассмотрим случай, когда у вас есть функция f1 что вызывает f2 что вызывает f3, Если f3 вызывает низкоуровневую асинхронную функцию с обратным вызовом, и если вы не используете волокна, вы должны включить f3 в асинхронную функцию с обратным вызовом, а затем путем заражения вы также должны повернуть f2 а также f1 в асинхронные функции. С волокнами вы можете сохранить f1, f2 а также f3 как обычные функции (без обратного вызова). Вам понадобится немного Fiber.yield() магия внутри f3 и вам также нужно будет позвонить f1 изнутри Fiber но вам не нужно беспокоиться о обратных вызовах в телах f1 а также f2,

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

Кроме того, Марсель, который писал волокна, рекомендует не использовать Fiber.yield() непосредственно в вашем коде, но вместо этого вы используете его библиотеку фьючерсов. С ней интересно играть Fiber.yield чтобы понять, из чего сделаны волокна, но я призываю вас использовать библиотеку фьючерсов для реального проекта. Это также поможет вам распараллелить ваш код.

Другие вопросы по тегам