1

我找不到任何好的文档(或关于 SO 的任何问题)来解释它的精确性yieldrun工作原理。

我无法找到异步方法如何能够使用Fibers/futures.

例如(代码在语法上不正确),我怎样才能让这个函数response同步返回

  function findData( param )
  {
    var fiber = Fiber( function(){
      var currentFiber = Fiber.current;
      Model.findOne({ "param" : param}, function (err, data) {
        response = { err : err, data : data };
      });
    });
    return fiber;
  }

就像是

  var value = findData("1");

Model是我从Mongoose模式类中获得的一个对象(不确定它是否相关)。

提前致谢。

4

2 回答 2

1

纤维不是新发明

节点纤程可以通过在最低级别以平台相关的方式保存当前执行环境的状态来暂停任何功能的运行(例如windows有纤程概念,没有广泛使用,比线程更轻量级,不先发制人)。

其他库使用语言特性模拟协同程序

所有其他 js 库通过使用回调函数来实现协程延续,将执行状态存储在范围变量中。这意味着您要么拥有回调金字塔、承诺链或 async/await(我将装饰生成器与 async/await 放在同一个桶中)。

纤维也是协同程序的一种可能实现。Fibers 应该很快,并且将它们集成到您的代码中不需要您以不同的代码样式编写或引入新的语法。可以从您自己的代码随意更改的执行上下文(堆栈、寄存器等)。

这不能在纯 JavaScript 中完成,node-fibers 使用本机库来实现这一点!

节点纤程限制您,因此您不会阻塞事件循环

节点纤程的具体概念是:javascript 事件循环在所有纤程之外,因此您的初始代码也可以在没有纤程的情况下运行。如果你有一个光纤参考,你可以通过fiber.run();. 当你在一个纤程中时,你可以通过调用放弃运行权Fiber.yield();(有效地暂停当前正在运行的代码),javascript事件循环将继续。所有内置回调(setTimeout, Promise.then, 事件处理程序,http 请求回调)都将在 javascript 事件循环中运行,无需光纤。

看这个例子

const Fiber = require("fibers");

function findDataAsync(param, callback) {
  setTimeout(() => {
    callback(null, "Async returned data");
  }, 100);
}

function findData( param ) {
  const currentFiber = Fiber.current;
  var response = null;

  findDataAsync(param, function (err, data) {
    response = { err : err, data : data };
    currentFiber.run();
  });
  Fiber.yield();
  if (response.err) {
    throw response.err;
  } else {
    return response.data;
  }
}


function main() {
  console.log("Inside fiber started");
  console.log(findData());
  console.log("Inside fiber finished");
}

console.log("Outside fiber started");
Fiber(main).run();
console.log("Outside fiber finished");

这应该输出:

Outside fiber started
Inside fiber started
Outside fiber finished
Async returned data
Inside fiber finished

请注意,Outside fiber finished在调用光纤中的第一个产量后立即记录。

如您所见,我们必须立即启动光纤才能yield. 如果您尝试在第三方库中使用纤程,则必须确保该库不会通过调用setTimeout或发出异步 http 请求将您当前的执行上下文“重置”到 javascript 事件循环。

于 2016-09-26T14:21:50.237 回答
1

将您的功能更改为:

function findData(param) {
  var currentFiber = Fiber.current;
  Model.findOne({ "param" : param }, function(err, data) {
    if (err) fiber.throwInto(err);
    else fiber.run(data);
  });
  return Fiber.yield();
}

然后你可以写:

function doSomething() {
  var param = ...;
  var data = findData(param);
  processData(data);
}

function doLotsOfThings() {
  ...;
  doSomething();
  doSomethingElse();
}

等等,等等……您可以像Model.findOne同步一样编写所有代码。

唯一的问题是您不能直接从节点的事件循环中调用任何这些函数。您必须在光纤内调用它们。通常,您将在 HTTP 侦听器(或 TCP 侦听器或其他)中创建纤程。典型代码:

http.createServer(function(request, response) {
  // you cannot call doLotsOfThings() here
  Fiber(function() {
    // but you can call it here
    try { doLotsOfThings(); }
    // and this the right place to catch exceptions too!
    catch (ex) { handleException(ex); }
  }).run();
}).listen(8124);

简而言之,您将Fiber.yield在调用异步函数(上面的第一个模式)时在低级别调用,并且您将在顶级侦听器中创建纤程(上面的第二个模式)。两者之间的所有代码都可以以同步方式编写。

注意:使用这些代码模式,您不需要在每个函数中捕获/测试错误。相反,您可以使用经典的结构化异常处理(让异常冒泡)。

于 2016-09-26T21:33:25.333 回答