运行 与 node-fibers 中的 yield 有什么区别

What is the difference between run vs yield in node-fibers

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

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

return 一个值

例如(代码在语法上不正确),我怎样才能让这个函数 return 与 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 模式 class 得到的一个对象(不确定它是否相关)。

提前致谢。

纤维不是新发明

Node fibers 可以通过在最低级别以平台相关的方式保存当前执行环境的状态来暂停任何功能的 运行ning(例如 windows 有一个fiber概念,应用不广泛,比线程更轻量,不抢占。

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

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

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

This cannot be done in pure JavaScript, node-fibers use native libraries to achieve this!

节点纤程限制你,这样你就不会阻塞事件循环

节点纤维的具体概念是:javascript 事件循环在所有纤维之外,因此您的初始代码 运行s 也没有纤维。如果你有纤维参考,你可以通过fiber.run();将权利运行传递给它。当你在一个fiber里面时,你可以通过调用Fiber.yield();放弃运行的权利(有效挂起当前的运行ning代码),javascript事件循环会继续。所有内置回调(setTimeoutPromise.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 后立即记录。

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

将您的函数更改为:

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,并且您将在顶级侦听器中创建纤程(上面的第二个模式)。中间的所有代码都可以写成sync风格。

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