国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > JavaScript > 正文

nodejs中的fiber(纖程)庫(kù)詳解

2019-11-20 12:52:23
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

fiber/纖程

在操作系統(tǒng)中,除了進(jìn)程和線(xiàn)程外,還有一種較少應(yīng)用的纖程(fiber,也叫協(xié)程)。纖程常常拿來(lái)跟線(xiàn)程做對(duì)比,對(duì)于操作系統(tǒng)而言,它們都是較輕量級(jí)的運(yùn)行態(tài)。通常認(rèn)為纖程比線(xiàn)程更為輕量,開(kāi)銷(xiāo)更小。不同之處在于,纖程是由線(xiàn)程或纖程創(chuàng)建的,纖程調(diào)度完全由用戶(hù)代碼控制,對(duì)系統(tǒng)內(nèi)核而言,是一種非搶占性的調(diào)度方式,纖程實(shí)現(xiàn)了合作式的多任務(wù);而線(xiàn)程和進(jìn)程則受內(nèi)核調(diào)度,依照優(yōu)先級(jí),實(shí)現(xiàn)了搶占式的多任務(wù)。另外,系統(tǒng)內(nèi)核是不知道纖程的具體運(yùn)行狀態(tài),纖程的使用其實(shí)是比較與操作系統(tǒng)無(wú)關(guān)。

在node中,單線(xiàn)程是僅針對(duì)javascript而言的,其底層其實(shí)充斥著多線(xiàn)程。而如果需要在javascript中實(shí)現(xiàn)多線(xiàn)程,一種常見(jiàn)的做法是編寫(xiě)C++ addon,繞過(guò)javascript的單線(xiàn)程機(jī)制。不過(guò)這種方法提升了開(kāi)發(fā)調(diào)試的難度和成本。像其他很多腳本語(yǔ)言,我們也可以把纖程的概念引入到node中。

node-fibers

node-fibers這個(gè)庫(kù)就為node提供了纖程的功能。多線(xiàn)程方面沒(méi)有測(cè)試出理想的結(jié)果,不過(guò)在異步轉(zhuǎn)同步作用顯著,也許在減少node調(diào)用堆棧、無(wú)限遞歸方面也會(huì)有價(jià)值可挖。本文檔主要介紹 node-fibers庫(kù)的使用方法和異步轉(zhuǎn)同步等內(nèi)容。

安裝

node-fibers是采用C語(yǔ)言編寫(xiě),直接下載源碼需要編譯,通常直接npm安裝即可:

復(fù)制代碼 代碼如下:

npm install fibers

fibers庫(kù)的使用

API

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

創(chuàng)建一個(gè)纖程,可以當(dāng)成構(gòu)造函數(shù)使用,也可以當(dāng)成普通函數(shù)調(diào)用。如下例:

復(fù)制代碼 代碼如下:

function fibo(n) {
    return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}
Fiber(function () {
    console.log(fibo(40));
});

當(dāng) run()調(diào)用的時(shí)候,纖程啟動(dòng),并為 fn分配新的堆棧, fn會(huì)在這個(gè)新的堆棧上運(yùn)行,直到 fn有返回值或調(diào)用 yield()。 fn返回后或調(diào)用 yield()后,堆棧重置,當(dāng)再次調(diào)用 run()時(shí),纖程會(huì)再次啟動(dòng), fn運(yùn)行于首次分配的堆棧中。

2.Fiber.current:

獲得當(dāng)前纖程,并可對(duì)其進(jìn)行操作。如果指定一個(gè)變量與其相關(guān)聯(lián),請(qǐng)務(wù)必確保此纖程能夠釋放,否則V8的垃圾回收機(jī)制會(huì)一直忽略這部分的內(nèi)存,造成內(nèi)存泄漏。

3.Fiber.yield(param):

前面的說(shuō)明中已經(jīng)提及過(guò)這個(gè)函數(shù)。 yield()方法用于中斷纖程,一定程度上類(lèi)似 return。一旦執(zhí)行 yield(),則此 Fiber中后續(xù)代碼將沒(méi)有機(jī)會(huì)執(zhí)行,例如:

復(fù)制代碼 代碼如下:

var fiber = Fiber(function () {
    console.log("Fiber Start");
    Fiber.yield();
    console.log("Fiber Stop");
}).run();
// 輸出: "Fiber Start"

執(zhí)行后只會(huì)輸出“Fiber Start”,后一個(gè)輸出命令沒(méi)有執(zhí)行。如果向 yield()傳入?yún)?shù),那么此參數(shù)作為 run()的返回值。

復(fù)制代碼 代碼如下:

var fiber = Fiber(function () {
    Fiber.yield("success");
}).run();
console.log(fiber); // -> "success"

4.Fiber.prototype.run(param):

這個(gè)方法已經(jīng)很熟悉了,之前隱約有提及調(diào)用 run()的兩種時(shí)態(tài),一是Fiber未啟動(dòng)時(shí),一時(shí)Fiber被yield時(shí)。在這兩種時(shí)態(tài)下, run()的行為并不太一樣。
當(dāng)Fiber未啟動(dòng)時(shí), run()接受一個(gè)參數(shù),并把它傳遞給 fn,作為其參數(shù)。當(dāng)Fiber處理yielding狀態(tài)時(shí), run()接受一個(gè)參數(shù),并把它作為 yield()的返回值,fn并不會(huì)從頭運(yùn)行,而是從中斷處繼續(xù)運(yùn)行。關(guān)于 fn、 yield、 run三者的參數(shù)、返回值等關(guān)系,可以通過(guò)下面的小例子來(lái)說(shuō)明:

復(fù)制代碼 代碼如下:

var Fiber = require('fibers');
var fiber = Fiber(function (a) {
    console.log("第一次調(diào)用run:");
    console.log("fn參數(shù)為:"+a);
    var b = Fiber.yield("yield");
    console.log("第二次調(diào)用run:");
    console.log("fn參數(shù)為:"+a);
    console.log("yield返回值為:"+b);
    return "return";
});
// 第一次運(yùn)行run()
var c=fiber.run("One");
// 第二次運(yùn)行run()
var d=fiber.run("Two");
console.log("調(diào)用yield,run返回:"+c);
console.log("fn運(yùn)行完成,run返回:"+d);

輸出如下:

復(fù)制代碼 代碼如下:

/*
第一次調(diào)用run:
fn參數(shù)為:One
第二次調(diào)用run:
fn參數(shù)為:One
yield返回值為:Two
調(diào)用yield,run返回:yield
fn運(yùn)行完成,run返回:return
*/

從上面例子中,可以很明顯看出 yield的使用方法與現(xiàn)在的javascript的語(yǔ)法相當(dāng)不同。在別的語(yǔ)言中(C#、Python等)已經(jīng)實(shí)現(xiàn)了 yield關(guān)鍵字,作為迭代器的中斷。不妨在node上也實(shí)現(xiàn)一個(gè)迭代器,具體體會(huì)一下 yield的使用。還是以開(kāi)頭的斐波那契數(shù)列為例:

復(fù)制代碼 代碼如下:

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());
}

輸出為:

復(fù)制代碼 代碼如下:

/*
1
1
2
3
5
8
13
21
34
55
*/

有兩個(gè)問(wèn)題需要留意,第一, yield說(shuō)是方法,更多地像關(guān)鍵字,與 run不同, yield不需要依托Fiber實(shí)例,而 run則需要。如果在Fiber內(nèi)部調(diào)用 run,則一定要使用: Fiber.current.run();第二, yield本身為javascript的保留關(guān)鍵字,不確定是否會(huì)、何時(shí)會(huì)啟用,所以代碼在將來(lái)可能會(huì)面臨變更。

5.Fiber.prototype.reset():

我們已經(jīng)知道Fiber可能存在不同的時(shí)態(tài),同時(shí)會(huì)影響 run的行為。而 reset方法則不管Fiber處理什么狀態(tài),都恢復(fù)到初始狀態(tài)。隨后再執(zhí)行 run,就會(huì)重新運(yùn)行 fn。

6.Fiber.prototype.throwInto(Exception):

本質(zhì)上 throwInto會(huì)拋出傳給它的異常,并將異常信息作為 run的返回值。如果在Fiber內(nèi)不對(duì)它拋出的異常作處理,異常會(huì)繼續(xù)冒泡。不管異常是否處理,它會(huì)強(qiáng)制 yield,中斷Fiber。

future庫(kù)的使用

在node中直接使用Fiber并不一直是合理的,因?yàn)镕iber的API實(shí)在簡(jiǎn)單,實(shí)際使用中難免會(huì)產(chǎn)生重復(fù)冗長(zhǎng)的代碼,不利于維護(hù)。推薦在node與Fiber之間增加一層抽象,讓Fiber能夠更好地工作。 future庫(kù)就提供了這樣一種抽象。 future庫(kù)或者任何一層抽象也許都不是完美的,沒(méi)有誰(shuí)對(duì)誰(shuí)錯(cuò),只有適用不適用。比如, future庫(kù)向我們提供了簡(jiǎn)單的API能夠完成異步轉(zhuǎn)同步的工作,然而它對(duì)封裝 generator (類(lèi)似上面的斐波那契數(shù)列生成器)則無(wú)能為力。

future庫(kù)不需要單獨(dú)下載安裝,已經(jīng)包含在 fibers庫(kù)中,使用時(shí)只需要 var future=require('fibers/future') 即可。

API

1.Function.prototype.future():

給 Function類(lèi)型添加了 future方法,將function轉(zhuǎn)化成一個(gè)“funture-function”。

復(fù)制代碼 代碼如下:

var futureFun = function power(a) {
    return a * a;
}.future();
console.log(futureFun(10).wait());

實(shí)際上 power方法是在Fibel內(nèi)執(zhí)行的。不過(guò)現(xiàn)有版本的 future有bug,官方?jīng)]有具體的說(shuō)明,如果需要使用此功能,請(qǐng)刪除掉 future.js的第339行和第350行。

2.new Future()

Future對(duì)象的構(gòu)造函數(shù),下文詳細(xì)介紹。

3.Future.wrap(fn, idx)

wrap方法封裝了異步轉(zhuǎn)同步的操作,是 future庫(kù)中對(duì)我們最有價(jià)值的方法。 fn表示需要轉(zhuǎn)換的函數(shù), idx表示 fn接受的參數(shù)數(shù)目,認(rèn)為其 callback方法為最后一個(gè)參數(shù)(這邊API的制定頗有爭(zhēng)議,有人傾向傳遞 callback應(yīng)該處于的位置,好在 wrap方法比較簡(jiǎn)單,可以比較容易修改代碼)。看一個(gè)例子就能了解 wrap的用法:

復(fù)制代碼 代碼如下:

var readFileSync = Future.wrap(require("fs").readFile);
Fiber(function () {
    var html = readFileSync("./1.txt").wait().toString();
    console.log(html);
}).run();

從這個(gè)例子中可以看出Fiber異步轉(zhuǎn)同步確實(shí)非常有效,除了語(yǔ)法上多了一步 .wait()外,其他已經(jīng) fs提供的 fs.readFileSync方法別無(wú)二致了。

4.Future.wait(futures):

這個(gè)方法前面已經(jīng)多次看到了。顧名思義,它的作用就是等待結(jié)果。如果要等待一個(gè)future的實(shí)例的結(jié)果,直接調(diào)用 futureInstance.wait()即可;如果需要等待一系列future實(shí)例的結(jié)果,則調(diào)用 Future.wait(futuresArray)。需要注意的是,在第二種用法中,一個(gè)future實(shí)例在運(yùn)行時(shí)出現(xiàn)錯(cuò)誤, wait方法不會(huì)拋出錯(cuò)誤,不過(guò)我們可以使用 get()方法直接獲取運(yùn)行結(jié)果。

5.Future.prototype.get():

get()的用法與 wait()的第一種方式很像,所不同的是, get()立刻返回結(jié)果。如果數(shù)據(jù)沒(méi)有準(zhǔn)備好, get()會(huì)拋出錯(cuò)誤。

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

上面的的 wrap方法總給人以一種 future其實(shí)在吞噬異步方法的回調(diào)函數(shù),并直接返回異步結(jié)果。事實(shí)上 future也通過(guò) resolve方法提供設(shè)置回調(diào)函數(shù)的解決方案。 resolve最多接受兩個(gè)參數(shù),如果只傳入一個(gè)參數(shù), future認(rèn)為傳了一個(gè)node風(fēng)格的回調(diào)函數(shù),例如如下示例:

復(fù)制代碼 代碼如下:

futureInstance.resolve(function (err, data) {
    if (err) {
        throw  err;
    } else {
        console.log(data.toString());
    }
});

如果傳入兩個(gè)參數(shù),則表示對(duì)錯(cuò)誤和數(shù)據(jù)分別做處理,示例如下:

復(fù)制代碼 代碼如下:

futureInstance.resolve(function (err) {
    throw err;
}, function (data) {
    console.log(data.toString());
});

另外 future并不區(qū)分 resolve的調(diào)用時(shí)機(jī),如果數(shù)據(jù)沒(méi)有準(zhǔn)備好,則將回調(diào)函數(shù)壓入隊(duì)列,由 resolver()方法統(tǒng)一調(diào)度,否則直接取數(shù)據(jù)立即執(zhí)行回調(diào)函數(shù)。

7.Future.prototype.isResolved():

返回布爾值,表示操作是否已經(jīng)執(zhí)行。

8.Future.prototype.proxy(futureInstance):

proxy方法提供一種 future實(shí)例的代理,本質(zhì)上是對(duì) resolve方法的包裝,其實(shí)是將一個(gè)instance的回調(diào)方法作為另一個(gè)instance的回調(diào)執(zhí)行者。例如:

復(fù)制代碼 代碼如下:

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

雖然執(zhí)行的是 proxy,但是最終 target的回調(diào)函數(shù)執(zhí)行了,并且是以 proxy的執(zhí)行結(jié)果驅(qū)動(dòng) target的回調(diào)函數(shù)。這種代理手段也許在我們的實(shí)際應(yīng)用中有很大作用,我暫時(shí)還沒(méi)有深入地思考過(guò)。

9.Future.prototype.return(value):

10.Future.prototype.throw(error):

11.Future.prototype.resolver():

12.Future.prototype.detach():

以上四個(gè)API呢我感覺(jué)相對(duì)于別的API,實(shí)際使用的場(chǎng)景或作用比較一般。 return和 throw都受 resolver方法調(diào)度,這三個(gè)方法都很重要,在正常的future使用流程中都會(huì)默默工作著,只是我沒(méi)有想出具體單獨(dú)使用它們的場(chǎng)景,所以沒(méi)有辦法具體介紹。 detach方法只能算 resolve方法的簡(jiǎn)化版,亦沒(méi)有介紹的必要。

發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 峨眉山市| 嘉义市| 额敏县| 邢台市| 奈曼旗| 普宁市| 泾源县| 梁山县| 西乌| 洛浦县| 台前县| 奎屯市| 监利县| 东乌珠穆沁旗| 南充市| 偏关县| 调兵山市| 都安| 景宁| 泸西县| 措美县| 军事| 葵青区| 郴州市| 磐石市| 留坝县| 萍乡市| 南木林县| 武清区| 宜都市| 义马市| 齐河县| 阜康市| 丹阳市| 乡城县| 玛曲县| 射洪县| 沐川县| 交口县| 文山县| 临朐县|