如何使用纤维与溪流

时间:2014-08-11 16:56:55

标签: javascript node.js node-fibers

我正在尝试使用带有光纤的光纤:

var Fiber = require('fibers');
var Future = require('fibers/future');
var fs = require('fs');

function sleepForMs(ms) {
  var fiber = Fiber.current;
  setTimeout(function() {
    fiber.run();
  }, ms);
  Fiber.yield();
}

function catchError(f, onError) {
  return function () {
    var args = arguments;
    var run = function () {
      try {
        var ret = f.apply(null, args);
      }
      catch (e) {
        onError(e);
      }
      return ret;
    };
    if (Fiber.current) {
      return run();
    }
    else {
      return Fiber(run).run();
    }
  }
}

function processFile(callback) {
  var count, finished, onData, onException, onIgnoredEntry;
  count = 0;
  finished = false;
  onException = function (error) {
    if (finished) {
      console.error("Exception thrown after already finished:", error.stack || error);
    }
    if (finished) {
      return;
    }
    finished = true;
    return callback(error);
  };
  onData = function(data) {
    console.log("onData");
    if (finished) {
      return;
    }
    console.log("before sleep");
    sleepForMs(500);
    console.log("after sleep");
    throw new Error("test");
  };
  return fs.createReadStream('test.js').on('data', catchError(onData, onException)).on('end', function() {
    console.log("end");
    if (finished) {
      return;
    }
    finished = true;
    return callback(null, count);
  }).on('error', function(error) {
    console.log("error", error);
    if (finished) {
      return;
    }
    finished = true;
    return callback(error);
  });
};

Fiber(function () {
  console.log("Calling processFile");
  Future.wrap(processFile)().wait();
  console.log("processFile returned");
}).run();
console.log("back in main");

但它确实不起作用。数据回调在回调内的光纤完成之前完成。所以上面的代码输出:

Calling processFile
back in main
onData
before sleep
end
processFile returned
after sleep
Exception thrown after already finished: Error: test

实际上它应该更像是:

Calling processFile
back in main
onData
before sleep
after sleep
end
processFile returned
Error: test

3 个答案:

答案 0 :(得分:0)

这是使用wait.for(Fibers的包装器)https://github.com/luciotato/waitfor

的实现

在此实现中,为每个数据块启动光纤,因此并行启动“n”个任务。在所有光纤完成之前,ProcessFile不会“返回”。

这是一个如何使用Fibers& amp; wait.for,但当然你应该在生产中使用它之前将模块级vars和所有函数封装在一个类中。

var wait = require('wait.for');
var fs = require('fs');

var tasksLaunched=0;
var finalCallback;
var callbackDone=false;
var dataArr=[]

function sleepForMs(ms,sleepCallback) {
  setTimeout(function() {
    return sleepCallback();
  }, ms);
}

function resultReady(err,data){

    if (err){
      callbackDone = true;
      return finalCallback(err);
    }

    dataArr.push(data);
    if (dataArr.length>=tasksLaunched && !callbackDone) {
      callbackDone = true;
      return finalCallback(null,dataArr);
    }
}

function processChunk(data,callback) {
    var ms=Math.floor(Math.random()*1000);
    console.log('waiting',ms);
    wait.for(sleepForMs,ms);
    console.log(data.length,"chars");
    return callback(null,data.length);
}

function processFile(filename,callback) {
  var count, onData, onException, onIgnoredEntry;
  count = 0;
  finalCallback = callback;

  onException = function (error) {
    if (!callbackDone){
      callbackDone = true;
      return callback(error);
    }
  };

  onData = function(data) {
    console.log("onData");
    tasksLaunched++;
    wait.launchFiber(processChunk,data,resultReady);
  };

  fs.createReadStream(filename)
    .on('data', onData)
    .on('end', function() {
        console.log("end");
    })
    .on('error', function(error) {
        console.log("error", error);
        if (!callbackDone) {
            callbackDone = true;
            return callback(error);
          }
    });
};

function mainFiber() {
  console.log("Calling processFile");
  var data = wait.for(processFile,'/bin/bash');
  console.log(data.length,"results");
  console.log("processFile returned");
};

//MAIN
wait.launchFiber(mainFiber);
console.log("back in main");

答案 1 :(得分:0)

减少休眠时间并为其他块设置一些优先级或定时器。 这样在经过一定的时间限制后,可以根据优先级显示块。 这就是你如何以你想要的方式获得输出。

答案 2 :(得分:0)

看起来没有人知道如何做你要问的事。

在这种情况下,您可以以某种传统的异步方式处理流,将结果函数应用于结果。

以下是一些如何操作的例子。


使用raw-body

收集所有流数据

一种解决方案是在处理任何流数据之前收集它们。可以使用raw-body module

轻松完成
var rawBody = require('raw-body');

function processData(data) {
  console.log("before sleep");
  sleepForMs(500);
  console.log("after sleep");
}

function processFile(callback) {
  var stream = fs.createReadStream('fiber.js');
  rawBody(stream, function(err, data) {
    if (err) return callback(err);
    Fiber(processData).run(data); // process your data
    callback();
  });
}

使用此示例,您将:

  1. 等待所有块到达
  2. 启动处理您在Fiber
  3. 中流式传输数据
  4. processData返回主线程
  5. 流数据将在未来的某个时间点处理
  6. 如果需要,您可以添加try ... catch或任何其他异常处理,以防止processData破坏您的应用。


    使用智能作业队列处理系列中的所有块

    但如果您真的想在到达时处理所有数据块,您可以使用一些智能控制流模块。以下是使用queue feature中的async module的示例:

    function processChunk(data, next) {
      return function() {
        console.log("before sleep");
        sleepForMs(500);
        console.log("after sleep");
        next();
      }
    }
    
    function processFile(callback) {
      var q = async.queue(function(data, next) {
        Fiber(processChunk(data, next)).run();
      }, 1);
      fs.createReadStream('fiber.js').on('data', function(data) {
        q.push(data);
      }).on('error', function(err) {
        callback(err);
      }).on('end', function() {
        callback(); // not waiting to queue to drain
      })
    }
    

    使用此示例,您将:

    1. 开始收听streampush每个新块到处理队列
    2. processData关闭时从stream返回主线程,而不是等待处理数据块
    3. 所有数据块将在时间的某个时刻以严格的系列进行处理

    4. 我知道这不是你所要求的,但我希望它会帮助你。