我來回答餓了么大前端的問題(2)

事件/異步

Promise

  • promise迷你書

    • Promise對象的三個狀態(tài) has-resolution, has-rejection, unresolved
    • .then() 方法是異步調(diào)用的。
    var promise = new Promise(function (resolve){
           console.log("inner promise"); // 1
           resolve(42);
    });
    promise.then(function(value){
           console.log(value); // 3
    });
    console.log("outer promise"); // 2
    
    // 輸出
    inner promise // 1
    outer promise // 2
    42            // 3
    
  • 其實在我的理解當(dāng)中丝格,同步是相對的撑瞧。對一串連續(xù)的事件使用promise封裝,他們之間是同步實行的显蝌,并不是對于外部预伺。我們再來解釋下這個例子:

    setTimeout(function() {
      console.log(1)
    }, 0);
    new Promise(function executor(resolve) {
      console.log(2);
      for( var i=0 ; i<10000 ; i++ ) {
        i == 9999 && resolve();
      }
      console.log(3);
    }).then(function() {
      console.log(4);
    });
    console.log(5);
    
    // 輸出
    2
    3
    5
    4
    1
    

    這里會用到 Event Loop 的知識,后面有詳細說明曼尊。首先代碼順序執(zhí)行酬诀,注冊了一個Timer事件,將Timer事件放入Timer隊列中骆撇。然后有一個promise被放入 poll 隊列中執(zhí)行瞒御。先是有一個輸出2的同步事件,被放入 poll 隊列執(zhí)行神郊,再就出現(xiàn)了 resolve() 肴裙,一個異步事件先放入i/o callback隊列中趾唱,再有一個輸出3的同步事件,被放入 poll 隊列執(zhí)行蜻懦。再是將輸出5的同步事件放入 poll 執(zhí)行鲸匿,poll 空了以后,將i/o callback中的 resolve() 放入poll 執(zhí)行阻肩,最后將Timer中的事件放入 poll 中執(zhí)行。

Events

Events中的emit是同步的运授,會按照注冊順序來觸發(fā)監(jiān)聽器烤惊。

一個事件監(jiān)聽器中監(jiān)聽同一個事件,會導(dǎo)致死循環(huán)吁朦?

const EventEmitter = require('events');

let emitter = new EventEmitter();

emitter.on('myEvent', () => {
  console.log('hi');
  emitter.emit('myEvent');
});

emitter.emit('myEvent');
  • 這種情況是會死循環(huán)的柒室,其實就是無限的遞歸調(diào)用,運行一下果然崩棧了逗宜。 Maximum call stack size exceeded
const EventEmitter = require('events');

let emitter = new EventEmitter();

emitter.on('myEvent', function sth () {
  emitter.on('myEvent', sth);
  console.log('hi');
});

emitter.emit('myEvent');
  • 通過源碼解析 Node.js 中 events 模塊里的優(yōu)化小細節(jié)
    • 這種情況不會出現(xiàn)死循環(huán)雄右,因為在執(zhí)行的過程當(dāng)中是把原監(jiān)聽數(shù)組拷貝一份出來執(zhí)行監(jiān)聽。當(dāng)在監(jiān)聽器中監(jiān)聽同一個事件的時候纺讲,只會在原監(jiān)聽數(shù)組當(dāng)中添加監(jiān)聽擂仍,而不會在這個拷貝后的數(shù)組添加。

Event Loop

  • JavaScript 運行機制詳解:再談Event Loop
    • 任務(wù)隊列

      • 所有同步任務(wù)都在主線程上執(zhí)行熬甚,形成一個執(zhí)行棧(execution context stack)逢渔。
      • 主線程之外,還存在一個"任務(wù)隊列"(task queue)乡括。只要異步任務(wù)有了運行結(jié)果肃廓,就在"任務(wù)隊列"之中放置一個事件。
      • 一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢诲泌,系統(tǒng)就會讀取"任務(wù)隊列"盲赊,看看里面有哪些事件。那些對應(yīng)的異步任務(wù)敷扫,于是結(jié)束等待狀態(tài)哀蘑,進入執(zhí)行棧,開始執(zhí)行葵第。
      • 主線程不斷重復(fù)上面的第三步递礼。
      • 所以異步任務(wù)永遠是在同步任務(wù)之后開始執(zhí)行的,不管他的代碼位置如何羹幸。
      • 排在任務(wù)隊列前面的事件優(yōu)先進入執(zhí)行棧脊髓,但是有些事件會被設(shè)置定時器,如果現(xiàn)在的時間小于了定時器的時間栅受,那么會一直等到到達定時器的時間才會將事件加入執(zhí)行棧将硝。如果現(xiàn)在的時間大于了定時器的時間恭朗,也不會立即將它加入執(zhí)行棧,會等到同步任務(wù)與它前面的異步任務(wù)執(zhí)行完成以后再將它加入執(zhí)行棧依疼。
    • process.nextTick

      • 在當(dāng)前"執(zhí)行棧"的尾部痰腮,下一次Event Loop(主線程讀取"任務(wù)隊列")之前,觸發(fā)回調(diào)函數(shù)律罢。也就是說膀值,它指定的任務(wù)總是發(fā)生在所有異步任務(wù)之前。
      • 如果有多個 process.nextTick 語句(不管它們是否嵌套)误辑,將全部在當(dāng)前"執(zhí)行棧"執(zhí)行沧踏。
    • setImmediate

      • 在當(dāng)前"任務(wù)隊列"的尾部添加事件,也就是說巾钉,它指定的任務(wù)總是在下一次Event Loop時執(zhí)行翘狱,這與 setTimeout(fn, 0) 很像。
      • process.nextTicksetImmediate 的一個重要區(qū)別:多個 process.nextTick 語句總是在當(dāng)前"執(zhí)行棧"一次執(zhí)行完砰苍,多個setImmediate可能則需要多次loop才能執(zhí)行完潦匈。事實上,這正是Node.js 10.0版添加 setImmediate 方法的原因赚导,否則像下面這樣的遞歸調(diào)用 process.nextTick茬缩,將會沒完沒了,主線程根本不會去讀取"事件隊列"吼旧!
  • Node.js Event Loop 的理解 Timers寒屯,process.nextTick()
    • 這一篇對于 Event Loop 的分析就更加進階了,我們首先看 Event Loop 的各個階段黍少。

         ┌───────────────────────┐
      ┌─>│        timers         │
      │  └──────────┬────────────┘
      │  ┌──────────┴────────────┐
      │  │     I/O callbacks     │
      │  └──────────┬────────────┘
      │  ┌──────────┴────────────┐
      │  │     idle, prepare     │
      │  └──────────┬────────────┘      ┌───────────────┐
      │  ┌──────────┴────────────┐      │   incoming:   │
      │  │         poll          │<─────┤  connections, │
      │  └──────────┬────────────┘      │   data, etc.  │
      │  ┌──────────┴────────────┐      └───────────────┘
      │  │        check          │
      │  └──────────┬────────────┘
      │  ┌──────────┴────────────┐
      └──┤    close callbacks    │
         └───────────────────────┘
      
      • timers: 執(zhí)行各種定時器預(yù)約的操作寡夹, setTimeout(callback)setInterval(callback)
      • I/O callbacks: 執(zhí)行除了 close 事件的callback和屬于 timerscallback以外的callback事件。
      • idle, prepare: 僅node內(nèi)部使用厂置。
      • poll: 獲取新的I/O事件, 適當(dāng)?shù)臈l件下node將阻塞在這里;(在我的理解中菩掏,這個階段才是一個 Event Loop 的開始)
      • check: 執(zhí)行 setImmediate() 設(shè)定的callbacks;
      • close callbacks: close 事件的回調(diào)會在該階段執(zhí)行。
      • 而還有一個獨立于 Event Loop 外的過程就是 process.nextTick() 它是在各個階段的切換階段進行調(diào)用昵济。
    • Poll階段

      • 代碼中沒有設(shè)置 timer
        • poll quenue 不為空時智绸,同步執(zhí)行隊列中的所有事件。直到隊列為空访忿,或執(zhí)行的callback達到系統(tǒng)的上限瞧栗。
        • poll quenue 為空時,如果設(shè)定了 setImmediate() 則進入 check階段海铆,沒有設(shè)定的話迹恐,就阻塞等待callback進入隊列。
      • 代碼中設(shè)置 timer
        • poll queue進入空狀態(tài)時卧斟,event loop 將檢查timers,如果有1個或多個timers時間時間已經(jīng)到達殴边,event loop 將按循環(huán)順序進入timers階段憎茂,并執(zhí)行timer queue.

進程

Process

  • process.cwd() :返回運行當(dāng)前腳本的工作目錄的路徑。
  • process.chdir() :改變工作目錄锤岸。

process.nextTick

這個在上面已經(jīng)分析過了竖幔,不贅述。

標(biāo)準(zhǔn)流

  • console.log 的實現(xiàn)

    exports.log = function() {
        process.stdout.write(format.apply(this, arguments) + '\n');
    };
    

    通過實現(xiàn)來說是偷,我覺得 console.log 是同步的拳氢。

  • 同步輸入的實現(xiàn) (nodeJS 中從命令行等待并讀入用戶輸入實現(xiàn)與用戶交互的方法):

    • 主要是思路是將 fs.readSync 的輸入流從定向到 process.stdin.fd
    • 我覺得還有一種方法是對輸入流進行監(jiān)聽
  • Linux 中關(guān)于進程管理的命令

    • topTOP 是一個動態(tài)顯示過程,即可以通過用戶按鍵來不斷刷新當(dāng)前狀態(tài).如果在前臺執(zhí)行該命令,它將獨占前臺,直到用戶終止該程序為止.比較準(zhǔn)確的說, top 命令提供了實時的對系統(tǒng)處理器的狀態(tài)監(jiān)視.它將顯示系統(tǒng)中CPU最“敏感”的任務(wù)列表.該命令可以按CPU使用.內(nèi)存使用和執(zhí)行時間對任務(wù)進行排序;而且該命令的很多特性都可以通過交互式命令或者在個人定制文件中進行設(shè)定.
    • psps 命令就是最基本同時也是非常強大的進程查看命令.使用該命令可以確定有哪些進程正在運行和運行的狀態(tài)蛋铆、進程是否結(jié)束馋评、進程有沒有僵尸、哪些進程占用了過多的資源等等戒职。總之大部分信息都是可以通過執(zhí)行該命令得到的透乾。
    • pstree: Linux pstree命令將所有行程以樹狀圖顯示洪燥,樹狀圖將會以 pid (如果有指定) 或是以 init 這個基本行程為根 (root),如果有指定使用者 id乳乌,則樹狀圖會只顯示該使用者所擁有的行程捧韵。

Child Process

child_process.forkPOSIXfork 有什么區(qū)別?

  • POSIXfork每天進步一點點——論fork()函數(shù)與Linux中的多線程編程
    • 當(dāng)程序調(diào)用 fork() 函數(shù)并返回成功之后,程序就將變成兩個進程汉操,調(diào)用 fork() 者為父進程再来,后來生成者為子進程。這兩個進程將執(zhí)行相同的程序文本磷瘤,但卻各自擁有不同的棧段芒篷、數(shù)據(jù)段以及堆棧拷貝采缚。子進程的棧针炉、數(shù)據(jù)以及棧段開始時是父進程內(nèi)存相應(yīng)各部分的完全拷貝,因此它們互不影響扳抽。如果fork成功篡帕,子進程中fork的返回值是0,父進程中fork的返回值是子進程的進程號贸呢,如果fork不成功镰烧,父進程會返回錯誤。
  • 但是現(xiàn)在沒有找到 child_process.fork 如果不是對當(dāng)前父進程的拷貝楞陷,那他的具體實現(xiàn)原理是什么怔鳖。

child.killchild.send 的區(qū)別

  • 在談這兩者的區(qū)別的時候是需要先談一下 forkspawn 的區(qū)別:NODEJS硬實戰(zhàn)筆記(多進程)
    • 實例化一個 spawn 后,會返回一個 ChildProcess 對象固蛾,該對象中包含了 stdin 败砂、stdoutstderr 流對象赌渣。而實例化一個 fork ,默認(rèn)是會繼承父進程的stdin 昌犹、stdoutstderr 流(當(dāng)然也可以打開)坚芜,并且會單獨建立一個IPC通道,使得父子進程之間可以通過監(jiān)聽 message 事件來進行通信斜姥,所以 fork 實際上是 spawn 的一個特例鸿竖。
      • send 是需要依賴 IPC 通道進行通信的,所以只有通過 fork 的子進程才能與父進程之間使用 send 通信的铸敏。kill 是通過信號系統(tǒng)來進行通信缚忧,只要操作系統(tǒng)支持信號系統(tǒng)就行。

孤兒進程與僵尸進程

子進程的結(jié)束和父進程的運行是一個異步過程,即父進程永遠無法預(yù)測子進程到底什么時候結(jié)束杈笔。

  • 孤兒進程:一個父進程退出闪水,而它的一個或多個子進程還在運行,那么那些子進程將成為孤兒進程蒙具。孤兒進程將被init進程(進程號為1)所收養(yǎng)球榆,并由init進程對它們完成狀態(tài)收集工作。 spawn 中可以通過 options.detached 指定父進程死亡后是否允許子進程存活禁筏。
    + 孤兒進程在被init進程接手以后持钉,init進程會循環(huán)地 wait() 它已經(jīng)退出的子進程,來進行善后篱昔,所以孤兒進程不會有什么危害每强。
  • 僵尸進程:一個進程使用 fork 創(chuàng)建子進程,如果子進程退出州刽,而父進程并沒有調(diào)用 waitwaitpid 獲取子進程的狀態(tài)信息空执,那么子進程的進程描述符仍然保存在系統(tǒng)中。這種進程稱之為僵尸進程穗椅。
    + 僵尸進程因為沒有被父進程調(diào)用 wait 脆烟,進程號、退出狀態(tài)房待、運行時間等都被保存在內(nèi)存中邢羔,特別是進程號一直被占用著,而系統(tǒng)的進程號又是有限的桑孩,所以大量的僵尸進程是會帶來非常大的威脅的拜鹤。

Cluster

round-robin

其實就是找下一個空閑的 worker,但是我們可以看看 Cluster 的進化過程流椒,從最開始的自由競爭(將引起 驚群效率)敏簿,將每個連接放到 worker 競爭到由 master 獲取連接再進行分配。

進程間通信

Linux進程間通信之管道(pipe)、命名管道(FIFO)與信號(Signal)

  • 從原理上惯裕,管道利用fork機制建立温数,從而讓兩個進程可以連接到同一個PIPE上。最開始的時候蜻势,讀入流和輸出流都連接在同一個進程Process 1上撑刺。當(dāng)fork復(fù)制進程的時候,會將這兩個連接也復(fù)制到新的進程(Process 2)握玛。隨后够傍,每個進程關(guān)閉自己不需要的一個連接 (一個關(guān)閉讀入流,一個關(guān)閉輸出流)挠铲,這樣冕屯,剩下的連接就構(gòu)成了PIPE。
  • 由于管道只能在父子進程之間進行通信拂苹,為了解決這一問題就提供了FIFO方法連接進程安聘,FIFO (First in, First out)為一種特殊的文件類型,它在文件系統(tǒng)中有對應(yīng)的路徑瓢棒。當(dāng)一個進程以讀(r)的方式打開該文件浴韭,而另一個進程以寫(w)的方式打開該文件,那么內(nèi)核就會在這兩個進程之間建立管道音羞。FIFO的好處在于我們可以通過文件的路徑來識別管道囱桨,從而讓沒有親緣關(guān)系的進程之間建立連接。
  • 信號是在軟件層次上對中斷機制的一種模擬,在原理上买决,一個進程收到一個信號與處理器收到一個中斷請求可以說是一樣的碌秸。信號是異步的,一個進程不必通過任何操作來等待信號的到達憾筏,事實上,進程也不知道信號到底什么時候到達。
    • 內(nèi)核給一個進程發(fā)送軟中斷信號的方法财边,是在進程所在的進程表項的信號域設(shè)置對應(yīng)于該信號的位。
    • 內(nèi)核處理一個進程收到的信號的時機是在一個進程從內(nèi)核態(tài)返回用戶態(tài)時点骑。

守護進程

通過系統(tǒng)命令:Linux 守護進程的啟動方法

  • 首先要明確酣难,通過使用 & 來啟動后臺任務(wù)的時候,實際上是不再繼承當(dāng)前session的 stdin 黑滴,會繼續(xù)繼承 stdoutstderr 憨募。
  • 然后需要明確當(dāng)用戶退出當(dāng)前session的時候,系統(tǒng)會向session發(fā)出 SIGHUP 信號袁辈,session再將該信號轉(zhuǎn)發(fā)給所有子進程菜谣,子進程收到后就會退出,所以普通的后臺進程是不能實現(xiàn)守護進程的需求的。
  • 那么實現(xiàn)的三個思路是:1. 不要讓session將 SIGHUP 信號轉(zhuǎn)發(fā)給后臺任務(wù)尾膊;2.將守護任務(wù)從后臺任務(wù)列表當(dāng)中移出媳危。但是光是這樣還是不行,因為后臺任務(wù)還是在繼承該session的 stdoutstderr冈敛,所以還需要將這兩個流重定向到外面待笑。那么第三個思路是與前兩個截然不同的,第三個是重建session莺债,將后臺任務(wù)啟動到這個新進程當(dāng)中滋觉。

通過node:Nodejs編寫守護進程

  • 創(chuàng)建一個進程A。
  • 在進程A中創(chuàng)建進程B齐邦,我們可以使用fork方式椎侠,或者其他方法。
  • 對進程B執(zhí)行 setsid 方法措拇。
    • Linux進程組和會話
    • 該進程變成一個新會話的會話領(lǐng)導(dǎo)我纪。
    • 該進程變成一個新進程組的組長。
    • 該進程沒有控制終端丐吓。
  • 進程A退出浅悉,進程B由init進程接管。此時進程B為守護進程券犁。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末术健,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子粘衬,更是在濱河造成了極大的恐慌荞估,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稚新,死亡現(xiàn)場離奇詭異勘伺,居然都是意外死亡,警方通過查閱死者的電腦和手機褂删,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門飞醉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人屯阀,你說我怎么就攤上這事缅帘。” “怎么了难衰?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵钦无,是天一觀的道長。 經(jīng)常有香客問我召衔,道長铃诬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮趣席,結(jié)果婚禮上兵志,老公的妹妹穿的比我還像新娘。我一直安慰自己宣肚,他們只是感情好想罕,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著霉涨,像睡著了一般按价。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上笙瑟,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天楼镐,我揣著相機與錄音,去河邊找鬼往枷。 笑死框产,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的错洁。 我是一名探鬼主播秉宿,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼屯碴!你這毒婦竟也來了描睦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤导而,失蹤者是張志新(化名)和其女友劉穎忱叭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗡载,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡窑多,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年仍稀,在試婚紗的時候發(fā)現(xiàn)自己被綠了洼滚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡技潘,死狀恐怖遥巴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情享幽,我是刑警寧澤铲掐,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站值桩,受9級特大地震影響摆霉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一携栋、第九天 我趴在偏房一處隱蔽的房頂上張望搭盾。 院中可真熱鬧,春花似錦婉支、人聲如沸鸯隅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝌以。三九已至,卻和暖如春何之,著一層夾襖步出監(jiān)牢的瞬間跟畅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工溶推, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留碍彭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓悼潭,卻偏偏與公主長得像庇忌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子舰褪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 名稱 libev - 一個 C 編寫的功能全面的高性能事件循環(huán)皆疹。 概要 示例程序 關(guān)于 libev Libev 是...
    hanpfei閱讀 15,269評論 0 5
  • 弄懂js異步 講異步之前,我們必須掌握一個基礎(chǔ)知識-event-loop占拍。 我們知道JavaScript的一大特點...
    DCbryant閱讀 2,711評論 0 5
  • 又來到了一個老生常談的問題略就,應(yīng)用層軟件開發(fā)的程序員要不要了解和深入學(xué)習(xí)操作系統(tǒng)呢? 今天就這個問題開始晃酒,來談?wù)劜?..
    tangsl閱讀 4,129評論 0 23
  • 研究生剛剛開學(xué)表牢,于我而言是個嶄新的開始,不論過去發(fā)生了什么贝次,我總應(yīng)該拋開往事崔兴,昂首挺胸向前闊步走。 可我今天一整天...
    小念君閱讀 576評論 5 1
  • ? ? ? ?
    dabaofu閱讀 965評論 0 0