我的编程空间,编程开发者的网络收藏夹
学习永远不晚

nodejs中的fiber(纤程)库详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

nodejs中的fiber(纤程)库详解

fiber/纤程

在操作系统中,除了进程和线程外,还有一种较少应用的纤程(fiber,也叫协程)。纤程常常拿来跟线程做对比,对于操作系统而言,它们都是较轻量级的运行态。通常认为纤程比线程更为轻量,开销更小。不同之处在于,纤程是由线程或纤程创建的,纤程调度完全由用户代码控制,对系统内核而言,是一种非抢占性的调度方式,纤程实现了合作式的多任务;而线程和进程则受内核调度,依照优先级,实现了抢占式的多任务。另外,系统内核是不知道纤程的具体运行状态,纤程的使用其实是比较与操作系统无关。

在node中,单线程是仅针对javascript而言的,其底层其实充斥着多线程。而如果需要在javascript中实现多线程,一种常见的做法是编写C++ addon,绕过javascript的单线程机制。不过这种方法提升了开发调试的难度和成本。像其他很多脚本语言,我们也可以把纤程的概念引入到node中。

node-fibers

node-fibers这个库就为node提供了纤程的功能。多线程方面没有测试出理想的结果,不过在异步转同步作用显著,也许在减少node调用堆栈、无限递归方面也会有价值可挖。本文档主要介绍 node-fibers库的使用方法和异步转同步等内容。

安装

node-fibers是采用C语言编写,直接下载源码需要编译,通常直接npm安装即可:

npm install fibers

fibers库的使用

API

1.Fiber(fn)/ new Fiber(fn):

创建一个纤程,可以当成构造函数使用,也可以当成普通函数调用。如下例:

function fibo(n) {

    return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;

}

Fiber(function () {

    console.log(fibo(40));

});

当 run()调用的时候,纤程启动,并为 fn分配新的堆栈, fn会在这个新的堆栈上运行,直到 fn有返回值或调用 yield()。 fn返回后或调用 yield()后,堆栈重置,当再次调用 run()时,纤程会再次启动, fn运行于首次分配的堆栈中。

2.Fiber.current:

获得当前纤程,并可对其进行操作。如果指定一个变量与其相关联,请务必确保此纤程能够释放,否则V8的垃圾回收机制会一直忽略这部分的内存,造成内存泄漏。

3.Fiber.yield(param):

前面的说明中已经提及过这个函数。 yield()方法用于中断纤程,一定程度上类似 return。一旦执行 yield(),则此 Fiber中后续代码将没有机会执行,例如:

var fiber = Fiber(function () {

    console.log("Fiber Start");

    Fiber.yield();

    console.log("Fiber Stop");

}).run();

// 输出: "Fiber Start"

执行后只会输出“Fiber Start”,后一个输出命令没有执行。如果向 yield()传入参数,那么此参数作为 run()的返回值。

var fiber = Fiber(function () {

    Fiber.yield("success");

}).run();

console.log(fiber); // -> "success"

4.Fiber.prototype.run(param):

这个方法已经很熟悉了,之前隐约有提及调用 run()的两种时态,一是Fiber未启动时,一时Fiber被yield时。在这两种时态下, run()的行为并不太一样。
当Fiber未启动时, run()接受一个参数,并把它传递给 fn,作为其参数。当Fiber处理yielding状态时, run()接受一个参数,并把它作为 yield()的返回值,fn并不会从头运行,而是从中断处继续运行。关于 fn、 yield、 run三者的参数、返回值等关系,可以通过下面的小例子来说明:

var Fiber = require('fibers');

var fiber = Fiber(function (a) {

    console.log("第一次调用run:");

    console.log("fn参数为:"+a);

    var b = Fiber.yield("yield");

    console.log("第二次调用run:");

    console.log("fn参数为:"+a);

    console.log("yield返回值为:"+b);

    return "return";

});

// 第一次运行run()

var c=fiber.run("One");

// 第二次运行run()

var d=fiber.run("Two");

console.log("调用yield,run返回:"+c);

console.log("fn运行完成,run返回:"+d);

输出如下:

从上面例子中,可以很明显看出 yield的使用方法与现在的javascript的语法相当不同。在别的语言中(C#、Python等)已经实现了 yield关键字,作为迭代器的中断。不妨在node上也实现一个迭代器,具体体会一下 yield的使用。还是以开头的斐波那契数列为例:

var fiboGenerator = function () {

    var a = 0, b = 0;

    while (true) {

        if (a == 0) {

            a = 1;

            Fiber.yield(a);

        } else {

            b += a;

            b == a ? a = 1 : a = b - a;

            Fiber.yield(b);

        }

    }

}

var f = new Fiber(fiboGenerator);

f.next = f.run;

for (var i = 0; i < 10; i++) {

    console.log(f.next());

}

输出为:

有两个问题需要留意,第一, yield说是方法,更多地像关键字,与 run不同, yield不需要依托Fiber实例,而 run则需要。如果在Fiber内部调用 run,则一定要使用: Fiber.current.run();第二, yield本身为javascript的保留关键字,不确定是否会、何时会启用,所以代码在将来可能会面临变更。

5.Fiber.prototype.reset():

我们已经知道Fiber可能存在不同的时态,同时会影响 run的行为。而 reset方法则不管Fiber处理什么状态,都恢复到初始状态。随后再执行 run,就会重新运行 fn。

6.Fiber.prototype.throwInto(Exception):

本质上 throwInto会抛出传给它的异常,并将异常信息作为 run的返回值。如果在Fiber内不对它抛出的异常作处理,异常会继续冒泡。不管异常是否处理,它会强制 yield,中断Fiber。

future库的使用

在node中直接使用Fiber并不一直是合理的,因为Fiber的API实在简单,实际使用中难免会产生重复冗长的代码,不利于维护。推荐在node与Fiber之间增加一层抽象,让Fiber能够更好地工作。 future库就提供了这样一种抽象。 future库或者任何一层抽象也许都不是完美的,没有谁对谁错,只有适用不适用。比如, future库向我们提供了简单的API能够完成异步转同步的工作,然而它对封装 generator (类似上面的斐波那契数列生成器)则无能为力。

future库不需要单独下载安装,已经包含在 fibers库中,使用时只需要 var future=require('fibers/future') 即可。

API

1.Function.prototype.future():

给 Function类型添加了 future方法,将function转化成一个“funture-function”。

var futureFun = function power(a) {

    return a * a;

}.future();

console.log(futureFun(10).wait());

实际上 power方法是在Fibel内执行的。不过现有版本的 future有bug,官方没有具体的说明,如果需要使用此功能,请删除掉 future.js的第339行和第350行。

2.new Future()

Future对象的构造函数,下文详细介绍。

3.Future.wrap(fn, idx)

wrap方法封装了异步转同步的操作,是 future库中对我们最有价值的方法。 fn表示需要转换的函数, idx表示 fn接受的参数数目,认为其 callback方法为最后一个参数(这边API的制定颇有争议,有人倾向传递 callback应该处于的位置,好在 wrap方法比较简单,可以比较容易修改代码)。看一个例子就能了解 wrap的用法:

var readFileSync = Future.wrap(require("fs").readFile);

Fiber(function () {

    var html = readFileSync("./1.txt").wait().toString();

    console.log(html);

}).run();

从这个例子中可以看出Fiber异步转同步确实非常有效,除了语法上多了一步 .wait()外,其他已经 fs提供的 fs.readFileSync方法别无二致了。

4.Future.wait(futures):

这个方法前面已经多次看到了。顾名思义,它的作用就是等待结果。如果要等待一个future的实例的结果,直接调用 futureInstance.wait()即可;如果需要等待一系列future实例的结果,则调用 Future.wait(futuresArray)。需要注意的是,在第二种用法中,一个future实例在运行时出现错误, wait方法不会抛出错误,不过我们可以使用 get()方法直接获取运行结果。

5.Future.prototype.get():

get()的用法与 wait()的第一种方式很像,所不同的是, get()立刻返回结果。如果数据没有准备好, get()会抛出错误。

6.Future.prototype.resolve(param1,param2):

上面的的 wrap方法总给人以一种 future其实在吞噬异步方法的回调函数,并直接返回异步结果。事实上 future也通过 resolve方法提供设置回调函数的解决方案。 resolve最多接受两个参数,如果只传入一个参数, future认为传了一个node风格的回调函数,例如如下示例:

futureInstance.resolve(function (err, data) {

    if (err) {

        throw  err;

    } else {

        console.log(data.toString());

    }

});

如果传入两个参数,则表示对错误和数据分别做处理,示例如下:

futureInstance.resolve(function (err) {

    throw err;

}, function (data) {

    console.log(data.toString());

});

另外 future并不区分 resolve的调用时机,如果数据没有准备好,则将回调函数压入队列,由 resolver()方法统一调度,否则直接取数据立即执行回调函数。

7.Future.prototype.isResolved():

返回布尔值,表示操作是否已经执行。

8.Future.prototype.proxy(futureInstance):

proxy方法提供一种 future实例的代理,本质上是对 resolve方法的包装,其实是将一个instance的回调方法作为另一个instance的回调执行者。例如:

var target = new Future;

target.resolve(function (err, data) {

    console.log(data)

});

var proxyFun = function (num, cb) {

    cb(null, num * num);

};

Fiber(function () {

    var proxy = Future.wrap(proxyFun)(10);

    proxy.proxy(target);

}).run(); // 输出100

虽然执行的是 proxy,但是最终 target的回调函数执行了,并且是以 proxy的执行结果驱动 target的回调函数。这种代理手段也许在我们的实际应用中有很大作用,我暂时还没有深入地思考过。

9.Future.prototype.return(value):

10.Future.prototype.throw(error):

11.Future.prototype.resolver():

12.Future.prototype.detach():

以上四个API呢我感觉相对于别的API,实际使用的场景或作用比较一般。 return和 throw都受 resolver方法调度,这三个方法都很重要,在正常的future使用流程中都会默默工作着,只是我没有想出具体单独使用它们的场景,所以没有办法具体介绍。 detach方法只能算 resolve方法的简化版,亦没有介绍的必要。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

nodejs中的fiber(纤程)库详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

nodejs中的fiber(纤程)库详解

fiber/纤程 在操作系统中,除了进程和线程外,还有一种较少应用的纤程(fiber,也叫协程)。纤程常常拿来跟线程做对比,对于操作系统而言,它们都是较轻量级的运行态。通常认为纤程比线程更为轻量,开销更小。不同之处在于,纤程是由线程或纤程创
2022-06-04

详解nodejs中的process进程

虽然node对操作系统做了很多抽象的工作,但是你还是可以直接和他交互,比如和系统中已经存在的进程进行交互,创建工作子进程。node是一个用于事件循环的线程,但是你可以在这个事件循环之外创建其他的进程(线程)参与工作。进程模块process模
2022-06-04

Nodejs中的this详解

以下内容都是关于在nodejs中的this而非javascript中的this,nodejs中的this和在浏览器中javascript中的this是不一样的。 在全局中的thisconsole.log(this); {} this.num
2022-06-04

NodeJs中的VM模块详解

什么是VM? VM模块是NodeJS里面的核心模块,支撑了require方法和NodeJS的运行机制,我们有些时候可能也要用到VM模板来做一些特殊的事情。 通过VM,JS可以被编译后立即执行或者编译保存下来稍后执行(JavaScript c
2022-06-04

详解nodejs中exports和module.exports的区别

require 用来加载代码,而 exports 和 module.exports 则用来导出代码。但很多新手可能会迷惑于 exports 和 module.exports 的区别,为了更好的理解 exports 和 module.expo
2022-06-04

详解IWinter 一个路由转控制器的 Nodejs 库

IWinter 是一个路由转控制器的 node 库,只解决一个问题:为了让使用者以更优雅的姿势进行路由的编写。支持在 Express 和 Koa 中使用。 项目地址:https://github.com/yvanwangl/iwinter
2022-06-04

NodeJS中的MongoDB快速入门详细教程

MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
2022-06-04

详解nodejs爬虫程序解决gbk等中文编码问题

使用nodejs写了一个爬虫的demo,目的是提取网页的title部分。 遇到最大的问题就是网页的编码与nodejs默认编码不一致造成的乱码问题。nodejs支持utf8, ucs2, ascii, binary, base64, hex等
2022-06-04

nodejs处理图片的中间件node-images详解

Cross-platform image decoder(png/jpeg/gif) and encoder(png/jpeg) for Node.jsnode.js轻量级跨平台图像编解码库var images = require("ima
2022-06-04

详解nodeJS中读写文件方法的区别

导言:nodejs中所有与文件相关的操作都在fs模块中,而读写操作又是我们会经常用到的操作,nodejs的fs模块针对读操作为我们提供了readFile,read, createReadStream三个方法,针对写操作为我们提供了write
2022-06-04

Pytorch中的torch.distributions库详解

这篇文章主要介绍了Pytorch中的torch.distributions库,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-02-24

nodejs中的读取文件fs模块示例详解

这篇文章主要为大家介绍了nodejs中的读取文件fs模块示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-12-19

C++11中的chrono库详解

C++11提供了日期时间相关的库chrono,通过chrono库可以很方便的处理日期和时间,这篇文章主要介绍了C++11中的chrono库,需要的朋友可以参考下
2023-03-19

在Debian(Raspberry Pi)树莓派上安装NodeJS的教程详解

在树莓派上运行NodeJS并不需要特别的配置,你只需要确保可以用openssh远程连接到你的树莓派。 安装并配置Open SSH服务器 它可以确保你能远程连接到树莓派,它应该已经被安装了,但是这个过程可以确保安装最新版和生成加密密钥。 如果
2022-06-04

详解如何在NodeJS项目中优雅的使用ES6

NodeJs最近的版本都开始支持ES6(ES2015)的新特性了,设置已经支持了async/await这样的更高级的特性。只是在使用的时候需要在node后面加上参数:--harmony。但是,即使如此node也还是没有支持全部的ES6特性。
2022-06-04

编程热搜

目录