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);
输出如下:
/*
第一次调用run:
fn参数为:One
第二次调用run:
fn参数为:One
yield返回值为:Two
调用yield,run返回:yield
fn运行完成,run返回:return
*/
从上面例子中,可以很明显看出 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());
}
输出为:
/*
1
1
2
3
5
8
13
21
34
55
*/
有两个问题需要留意,第一, 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方法的简化版,亦没有介绍的必要。
转载于:https://www.cnblogs.com/youlechang123/p/6812361.html
nodejs中的fiber(纤程)库详解相关推荐
- matlab中sinks,MATLAB Simulink模块库详解(二)Sinks篇
MATLAB Simulink模块库详解(二)Sinks篇 Simulink模块库概述 1.Sources模块库,为仿真提供各种信号源 2.Sinks模块库,为仿真提供输出设备元件 3.Continu ...
- python协程库_python中协程的详解(附示例)
本篇文章给大家带来的内容是关于python中协程的详解(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 协程,又称微线程,纤程.英文名Coroutine 协程看上去也是子程序 ...
- python时间函数报错_python3中datetime库,time库以及pandas中的时间函数区别与详解...
1介绍datetime库之前 我们先比较下time库和datetime库的区别 先说下time 在 Python 文档里,time是归类在Generic Operating System Servic ...
- python获取系统时间函数_python3中datetime库,time库以及pandas中的时间函数区别与详解...
1介绍datetime库之前 我们先比较下time库和datetime库的区别 先说下time 在 Python 文档里,time是归类在Generic Operating System Servic ...
- python3中urllib库_Python--urllib3库详解
Python--urllib3库详解 Urllib3是一个功能强大,条理清晰,用于HTTP客户端的Python库,许多Python的原生系统已经开始使用urllib3.Urllib3提供了很多pyth ...
- python gpu加速库比matlab快吗_Python之Unittest和Requests库详解
1.按类来执行 import unittest class f1(unittest.TestCase): def setUp(self): pass def tearDown(self): pass ...
- 合宙lua库详解-socket
文章目录 简介 START 建立连接 发送数据 接收数据 关闭连接 主动关闭 被动关闭 简介 作为通讯的基础,只要你上网就需要用到lua的socket模块,即使使用mqtt,http等其它模块间接也调 ...
- (转)树莓派wiringPi库详解
https://www.cnblogs.com/lulipro/p/5992172.html <div id="post_detail"> 树莓派wiringPi库详解 ...
- python中文读音ndarray-numpy中的ndarray方法和属性详解
NumPy数组的维数称为秩(rank),一维数组的秩为1,二维数组的秩为2,以此类推.在NumPy中,每一个线性的数组称为是一个轴(axes),秩其实是描述轴的数量.比如说,二维数组相当于是一个一维数 ...
最新文章
- 请求一个action,将图片的二进制字节字符串在视图页面以图片形式输出
- Java基础super关键字、final关键字、static关键字、匿名对象整理
- SQL Server 2005数据库日志文件损坏的情况下如何恢复数据库
- Oracle数据库入门——常用的数据字典
- “睡服”面试官系列第十八篇之generator函数的语法(建议收藏学习)
- 思维导图下载 注册安全_【思维导图】初中各科思维导图,涵盖3年各科所有知识点,可下载打印!...
- DOS批处理高级教程精选(二)
- 【c语言】关键字存储类型讲解(auto,extern,static,register,const)
- md5碰撞Java_什么是导致MD5碰撞的最短字符串?
- autobuddy in mfc导致的错误
- 破解visio2013记录
- SiTime 硅晶振抖动定义和测量方法
- php简易聊天室系统,php实现简易聊天室应用代码
- 在Ubuntu Kylin 优麒麟系统中安装 Etcher镜像烧录软件
- 安装一级计算机失败,Win7/Win8.1升级Win10提示“安装失败”的解决方法
- 赵某于2011年3月23夜被捕获
- 非负数的和与个数c语言编程,C语言习题中,编程实现从键盘任意输入20个整数,统计非负数个数,并计算非负数之和...
- 简要介绍SIGMOD2021接收的区块链相关论文
- 基于多传感器数据融合的全自动泊车系统研究与应用(开题报告)
- arch yaourt安装
热门文章
- Java 多态的简单介绍.
- [转] fedora linux下安装vmware tools的最好方法
- php grid 分页,jqGrid实现前端分页
- ip中继对接_鼎信通达MTG系列-语音中继网关,可和VOS,IPPBX,SIP网关进行对接
- 这些 Linux 的“自动化”技巧,教你轻松完成任务
- 2014年江苏省计算机二级c语言考试大纲,2009年江苏省计算机等级考试二级C语言考试大纲...
- anaconda3配置环境变量_阿里云ECS配置Jupyter Notebook
- 单片机控制lcd序c语言,51单片机驱动1602液晶的C程序
- java 开源记账_生鲜配送系统ERP(JAVA开源版)-水产记账ipad
- dva处理_umi项目中dvaeffects异常统一处理