nodejs中異步

1 nodejs 中的異步存在嗎伤为?

現(xiàn)在有點(diǎn) javascript 基礎(chǔ)的人都在聽說過 nodejs ,而只要與 javascript 打交到人都會用或者是將要使用 nodejs 据途。畢竟 nodejs 的生態(tài)很強(qiáng)大绞愚,與 javascript 相關(guān)的工具也做的很方便,很好用颖医。

javascript 語言很小巧位衩,但是一旦與 nodejs 中的運(yùn)行環(huán)境放在一起,有些概念就很難理解熔萧,特別是異步的概念糖驴。有人會說不會啊僚祷,很好理解啊贮缕?不就是一個ajax請求加上一個回調(diào)函數(shù)辙谜,這個ajax函數(shù)就是能異步執(zhí)行的函數(shù),他在執(zhí)行完了就會調(diào)用回調(diào)函數(shù)感昼,我承認(rèn)這個樣做是很容易装哆,早些時候我甚至認(rèn)為在 javascript 中加了回調(diào)函數(shù)的函數(shù)都可以異步的,異步和回調(diào)函數(shù)成對出現(xiàn)定嗓。多么荒謬的理解巴汕佟!

直到有一天宵溅,我在寫程序時想到一個問題:在 nodejs 中在不調(diào)用系統(tǒng)相關(guān) I/O 凌简,不調(diào)用 c++ 寫的 plugin 的情況下,寫一個異步函數(shù)恃逻?我查了資料雏搂,有人給我的答案是調(diào)用 setTimeout(fn,delay) 就變成了異步了。但是我還是不明白為什么要調(diào)用這樣一個函數(shù)寇损,這個函數(shù)的語義跟async完全不一樣畔派,為什么這樣就行?

帶著這個疑問润绵,我查了很多資料,包括官方文檔胞谈,代碼尘盼,別人的blog。慢慢的理解烦绳,最后好像是知道了為什么會是這樣卿捎,整篇文章就是對所了解東西的理解。懇請大家批評指正径密。

說明:nodejs 的文檔是用的 v5.10.1 API午阵,而代碼方面:nodejs 和 libuv 是用的 master 分支。

2 nodejs 的架構(gòu)基礎(chǔ)

在探索 nodejs 的異步時享扔,首先需要對 nodejs 架構(gòu)達(dá)成統(tǒng)一認(rèn)識:

  1. nodejs 有 javascript 的運(yùn)行環(huán)境底桂,目前它的實(shí)現(xiàn)是 chrome 的 V8 引擎。
  2. nodejs 基于事件驅(qū)動和非阻塞 I/O 模型惧眠,目前它的實(shí)現(xiàn)是 libuv籽懦。
  3. 當(dāng)前的 libuv 是多線程的,文檔中有說明氛魁。
  4. nodejs 在運(yùn)行時只生成了一個 javascript 運(yùn)行環(huán)境 的實(shí)例暮顺,也就是說 javascript 解釋器只有一個厅篓。
  5. nodejs 在主線程中調(diào)用 V8 引擎的實(shí)例執(zhí)行 javascript 代碼。

如果以上 5 點(diǎn)你不認(rèn)同的話捶码,那下面就不需要看了羽氮,看了會覺得漏洞百出。

上面的 5 點(diǎn)主要說明另一層意思了:

  1. nodejs 的 javascript 運(yùn)行環(huán)境可以換惫恼,在 nodejs 官方 github
    中 PR档押,可以看這個,微軟想把 javascript 運(yùn)行環(huán)境換成自己家的尤筐。
  2. nodejs 的事件驅(qū)動和非阻塞 I/O 模型也可以換汇荐,目前來看 libuv 運(yùn)行的不錯,大家都很高興盆繁。另外掀淘,你可能不知道,chromium 和 chrome 中使用了另一個實(shí)現(xiàn) libevent2油昂,證據(jù)在這里:鏈接革娄。
  3. nodejs 不是單線程,它是多線程程序冕碟,因?yàn)?libuv 就已經(jīng)是多線程了拦惋。
  4. 因?yàn)槭乔度胧?js 引擎,只能調(diào)用宿主環(huán)境中提供的方法安寺。當(dāng)前來說厕妖,nodejs 主要把 libuv 的 io/timer 接口提供給了 js 引擎,其他的沒有提供(包括 libuv 的工作線程)挑庶。
  5. nodejs 也沒有提供給 js引擎 新建調(diào)用系統(tǒng)線程的任何方法言秸,所以在nodejs中執(zhí)行 javascript,是沒有辦法新開線程的迎捺。
  6. js 引擎只有一個實(shí)例且在 nodejs 的主線程中調(diào)用举畸。

結(jié)論

  1. nodejs 中存在異步,集中在 I/O 和 Timer 調(diào)用這一塊凳枝,其他的地方?jīng)]有抄沮。
  2. js 引擎沒有異步或者并行執(zhí)行可能,因?yàn)?js 引擎是在 nodejs 的主線程調(diào)用岖瑰,所以 js 引擎執(zhí)行的 javascript 代碼都是同步執(zhí)行叛买,沒有異步執(zhí)行。所以你想寫出來一個不調(diào)用 I/O和 的異步方法蹋订,不可能聪全。

那nodejs中常談的異步回調(diào)是怎么回事?

3 nodejs 中的回調(diào)和異步的關(guān)系是什么辅辩?

在 javascript 中使用回調(diào)函數(shù)可所謂登峰造極难礼,基本上所有的異步函數(shù)都會要求有一個回調(diào)函數(shù)娃圆,以至于寫 javascript 寫多了,看到回調(diào)函數(shù)的接口蛾茉,都以為是異步的調(diào)用讼呢。

但是真相是回調(diào)函數(shù),只是javascript 用來解決異步函數(shù)調(diào)用如何處理返回值這個問題的方法谦炬,或這樣來說:異步函數(shù)調(diào)用如何處理返回值這個問題上悦屏,在系統(tǒng)的設(shè)計(jì)方面而言,有很多辦法键思,而 nodejs 選擇了 javascript 的傳統(tǒng)方案础爬,用回調(diào)函數(shù)來解決這個問題

這個選擇好不好吼鳞,我認(rèn)為在當(dāng)時來說看蚜,很合適。但隨著 javascript 被用來寫越來越大的程序赔桌,這個選擇不是一個好的選擇供炎,因?yàn)榛卣{(diào)函數(shù)嵌套多了真的很難受,我覺得主要是很難看疾党,(就跟 lisp 的 )))))))))))) )音诫,讓一般人不好接受,現(xiàn)在情況改善多了雪位,因?yàn)橛辛薖romise竭钝。

結(jié)論

  1. 回調(diào)函數(shù)與異步?jīng)]有關(guān)系,只是在 javascript 中用來解決異步的返回值的問題雹洗,所以異步函數(shù)必須帶一個回調(diào)函數(shù)蜓氨,他們成對出現(xiàn),讓人以為有關(guān)系队伟。
  2. 在 javascript 中有回調(diào)不一定是異步函數(shù),而異步必須帶一個回調(diào)函數(shù)幽勒。

4 nodejs 中怎樣解決異步的問題嗜侮?

前面也說了,nodejs 的 js 引擎不能異步執(zhí)行 javascript 代碼啥容。那js中我們常使用的異步是什么意思的锈颗?

答案分為兩部分:

第一部分:與I/O和timer相關(guān)的任務(wù),js引擎確實(shí)是異步咪惠,調(diào)用時委托 libuv 進(jìn)行 I/O 和timer 的相關(guān)調(diào)用击吱,好了之后就通知 nodejs,nodejs 然后調(diào)用 js 引擎執(zhí)行 javascript 代碼遥昧;

第二部分:其它部分的任務(wù)覆醇,js 引擎把異步概念(該任務(wù)我委托別人執(zhí)行朵纷,我接著執(zhí)行下面的任務(wù),別人執(zhí)行完該任務(wù)后通知我)弱化成稍后執(zhí)行(該任務(wù)我委托自己執(zhí)行但不是現(xiàn)在永脓,我接著執(zhí)行下面的任務(wù)袍辞,該任務(wù)我稍后會自己執(zhí)行,執(zhí)行完成后通知我自己)的概念常摧。

這就是 js 引擎中異步的全部意思搅吁。基本上等同我們常說的:我馬上做這件事落午。不過還是要近一步解釋一下第二部分:

  1. 任務(wù)不能委托給別人谎懦,都是自己做。
  2. 如果當(dāng)前我做的事件需要很長時間溃斋,那我馬上要做的事一直推遲界拦,等我做了完手頭這件事再說。

nodejs 中 js 引擎把異步變成了稍后執(zhí)行盐类,使寫 javascript 程序看起來像異步執(zhí)行寞奸,但是并沒有減少任務(wù),因此在 javascript 中你不能寫一個需要很長時間計(jì)算的函數(shù)(計(jì)算Pi值1000位在跳,大型的矩陣計(jì)算)枪萄,或者在一個tick(后面會說)中執(zhí)行過多的任務(wù),如果你這樣寫了猫妙,整個主線程就沒有辦法響應(yīng)別的請求瓷翻,反映出來的情況就是程序卡了,當(dāng)然如果非要寫當(dāng)然也有辦法割坠,需要一些技巧來實(shí)現(xiàn)齐帚。

而 js 引擎稍后執(zhí)行稍后到底是多久,到底執(zhí)行哪些任務(wù)彼哼?這些問題就與 nodejs 中四個重要的與時間有關(guān)的函數(shù)有關(guān)了对妄,他們分別是:setTimeout,setInterval敢朱,process.nextTick剪菱,setImmediate。下面簡單了解一下這四個函數(shù):

setTimeout 和 setInterval

setImeout 主要是延遲執(zhí)行函數(shù)拴签,其中有一個比較特別的調(diào)用:setTimeout(function(){/* code */},0)孝常,經(jīng)常見使用,為什么這樣使用看看后面蚓哩。還有 setInterval 周期性調(diào)用一個函數(shù)构灸。

setImmediate 和 process.nextTick

setImmediate 的意思翻譯過來是立刻調(diào)用的意思,但是官方文檔的解釋是:

Schedules "immediate" execution of callback after I/O events' callbacks and before timers set by setTimeout and setInterval are triggered.

翻譯過來大意就是:被 setImmediate 的設(shè)置過的函數(shù)岸梨,他的執(zhí)行是在 I/O 事件的回調(diào)執(zhí)行之后喜颁,在 計(jì)時器觸發(fā)的回調(diào)執(zhí)行之前稠氮,也就是說在 setTimeout 和 setInterval 之前,好吧這里還有一個順序之分洛巢。

process.nextTick 可就更怪了括袒。官方的意思是:

It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.

翻譯過來大意就是:他運(yùn)行在任何的 I/O 和定時器的 subsequent ticks 之前。

又多了很多的概念稿茉,不過別慌锹锰,在下面會講 nodejs 的EventLoop,這里講的很多的不理解地方就會在 EventLoop 中講明白漓库。

5 nodejs 中神秘的 EventLoop

EvevtLoop大體上來說就是一個循環(huán)恃慧,它不停的檢查注冊到他的事件有沒有發(fā)生,如果發(fā)生了渺蒿,就執(zhí)行某些功能痢士,一次循環(huán)通常叫tick。這里有講EventLoop茂装,還有這里怠蹂。

在 nodejs 中也存在這樣一個 EventLoop,不過它是在 libuv 中少态。它每一次循環(huán)叫 tick城侧。而在每一次 tick 中會有不同的階段,每一個階段可以叫 subTick彼妻,也就說是這個tick的子tick嫌佑,libuv就有很多的子 tick,如I/O 和定時器等侨歉。下面我用一張圖來表示一下屋摇,注意該循環(huán)一直在 nodejs 的主線程中運(yùn)行:

    +-------------+
    |             |
    |             |
    |       +-----v----------------------+
    |       |                            |
    |       | uv__update_time(loop)      |  subTick
    |       |                            |
    |       +-----+----------------------+
    |             |
    |             |
    |       +-----v----------------------+
    |       |                            |
    |       | uv__run_timers(loop)       |  subTick
    |       |                            |
tick|       +-----+----------------------+
    |             |
    |             |
    |       +-----v----------------------+
    |       |                            |
    |       | uv__io_poll(loop, timeout) |  subTick
    |       |                            |
    |       +-----+----------------------+
    |             |
    |             |
    |       +-----v----------------------+
    |       |                            |
    |       | uv__run_check(loop)        |  subTick
    |       |                            |
    |       +-----+----------------------+
    |             |
    |             |
    |             |
    +-------------+

以上的流程圖已經(jīng)進(jìn)行了裁減,只保留重要的內(nèi)容幽邓,如果你想詳細(xì)了解炮温,可在 libuv/src/unix/core.cc,第334行:uv_run函數(shù)進(jìn)行詳細(xì)了解牵舵。

下面來解釋一下各個階段的作用:

uv__update_time是用來更新定時器的時間柒啤。uv__run_timers是用來觸發(fā)定時器,并執(zhí)行相關(guān)函數(shù)的地方棋枕。uv__io_poll是用來 I/O觸發(fā)后執(zhí)行相關(guān)函數(shù)的地方。
uv__run_check的用處代碼中講到妒峦。

了解到 nodejs 中 EventLoop 的執(zhí)行階段后重斑,需要更深一步了解在 nodejs 中 js引擎和EvevtLoop是如何被整合在一起工作的。以下是一些偽代碼肯骇,它用來說明一些機(jī)制窥浪。

不過你需要知道在 nodejs 中 setTimeout祖很、setInterval、setImmediate和process.nextTick都是系統(tǒng)級的調(diào)用漾脂,也就是他們都是c++ 來實(shí)現(xiàn)的假颇。setTimeout和setInterval 可看看這個文件:timer_wrap.cc。另外兩個我再補(bǔ)吧骨稿。

class V8Engine {
  let _jsVM;
  
  V8Engine(){
     _jsVM = /*js 執(zhí)行引擎 */;
  }
  
  void invoke(handlers){
  // 依次執(zhí)行笨鸡,直到 handlers 為空
    handlers.forEach(handler,fun => _jsVM.run(handler));
  }
}

class EvenLoop {
  let _jsRuntime = null;
  let _callbackHandlers = []; 【1】
  let _processTickHandlers = []; 【2】
  let _immediateHandlers = []; 【3】

  // 構(gòu)造函數(shù)
  EvenLoop(jsRuntime){
   _jsRuntime = jsRuntime;
  }

  void start(){
    where(true){
      _jsRuntime.invoke(_processTickHandlers); 【4】
      _processTickHandlers.clear();

      update_time();
      run_timer(); 
      run_pool();
      run_check();

      if (process.exit){
        _jsRuntime.invoke(_processTickHandlers); 【5】
        _processTickHandlers.clear();
        break;
      }
    }
  }

  void update_time(){
      //  更新 timer 的時間
  }

  void run_timer(){ 【6】
    let handlers = getTimerHandler(); 
    _callbackHandlers.push(handlers);
    _jsRuntime.invoke(_callbackHandlers);
    _jsRuntime.invoke(_processTickHandlers);
    _callbackHandlers.clear();
    _processTickHandlers.clear();
  }

  void run_pool(){  【6】
    let handlers = getIOHandler(); 
    _callbackHandlers.push(handlers);
    _jsRuntime.invoke(_callbackHandlers);
    _jsRuntime.invoke(_processTickHandlers);
    _callbackHandlers.clear();
    _processTickHandlers.clear();
  }

  void run_check(){  【7】
    let handlers = getImmediateHandler();
    _immediateHandlers.push(handlers);
    _jsRuntime.invoke(_immediateHandlers);
    _immediateHandlers.clear();
  }
 
}

main(){
  JsRuntime jsRuntime = new V8Engine();
  EventLoop eventLoop = new EventLoop(jsRuntime);
  eventLoop.start();
}

// 主線程中執(zhí)行
main();

以上代碼是 nodejs 的粗略的執(zhí)行過程,還想進(jìn)一步了解坦冠,可以看這從入口函數(shù)看起:node_main.cc

按標(biāo)號進(jìn)行說明:

  1. 全局的回調(diào)事件先進(jìn)先出隊(duì)列形耗,包括了 I/O 事件和 Timer 事件的回調(diào)對象。
  2. 全局的nextTick的回調(diào)對象先進(jìn)先出隊(duì)列辙浑。
  3. 全局的setImmediate的回調(diào)對象先進(jìn)先出隊(duì)列激涤。
  4. 開始時會執(zhí)行 nextTick的隊(duì)列。
  5. 程序退出時會執(zhí)行 nextTick的隊(duì)列判呕。
  6. 可以看出nextTick隊(duì)列會在run_timerrun_pool之后執(zhí)行倦踢√钐В回到第三節(jié)說的nextTick的執(zhí)行時機(jī)癌淮,看出來該隊(duì)列確實(shí)會在 I/O 和 Timer 之前運(yùn)行。在文檔中特別說明如果你遞歸調(diào)用 nextTick 會阻 I/O 事件的調(diào)用就像調(diào)用了 loop鳍贾。依照上面的偽代碼梦抢,發(fā)現(xiàn)如果你遞歸調(diào)用nextTick,那nextTick回調(diào)對象先進(jìn)先出隊(duì)列就不會為空般贼,js 引擎就一直在執(zhí)行,影響之后的代碼執(zhí)行奥吩。
  7. setImmediate 回調(diào)對象先進(jìn)先出隊(duì)列哼蛆,每一次 tick 就執(zhí)行一次。

可以從代碼中看出這四個時間函數(shù)執(zhí)行時機(jī)的區(qū)別霞赫,而setTimeout(fn,0)是在 _callbackHandlers的隊(duì)列中腮介,而setImmediate,還有 nextTick 都在不同的隊(duì)列中執(zhí)行端衰。

總體來說叠洗,nextTick執(zhí)行最快,而setTmmediate能保證每次tick都執(zhí)行旅东,而setTimeout是 libuv 的 Timber 保證灭抑,可能會有所延遲。

相關(guān)鏈接

  1. 有人覺得得 process.nextTick 名不副實(shí)抵代,得改個名字腾节,變成 process.currentTick,沒有通過,理由是太多的代碼依賴這個函數(shù)了案腺,沒有辦法改名字庆冕,這里
  2. 如果你覺得 EventLoop 我說的不清楚劈榨,你還可以看看這篇博客:鏈接访递。
  3. 如果你覺得 setImmediate 和 nextTick 說的不清楚,可以看這:鏈接同辣。
  4. 這個也可以:鏈接拷姿。
  5. Synchronously asynchronous
  6. designing-apis-for-asynchrony邑闺。

6 nodejs 回調(diào)和大數(shù)據(jù)與大計(jì)算量的解決方案

回調(diào)解決方案- promise

我相信你一但用了promise跌前,你就回不去以往的回調(diào)時代,promise 非常好使用陡舅,強(qiáng)列推薦使用抵乓。如果你還想了解promise怎么實(shí)現(xiàn)的,我給你透個底靶衍,必不可少setTimeout這個函數(shù)灾炭,可以參考 Q promise的設(shè)計(jì)文檔,還有一步步來手寫一個Promise也不錯颅眶。

大數(shù)據(jù)與大計(jì)算量的解決方案 - 分片數(shù)據(jù)或者分片計(jì)算

如果要寫一個處理數(shù)據(jù)量很大的任務(wù)蜈出,我想這個函數(shù)可以給你思路:

yielding processes

function chunk(array,process,context){
  setTimeout(function(){
    var item = array.shift();
    process.call(context,item);

    if (array.length >0){
      setTimeout(arguments.callee,100);
    }
  },100)
}

函數(shù)節(jié)流

如果要寫一個計(jì)算量很大的任務(wù)涛酗,這個函數(shù)也可以給你思路:

var process = {
  timeout = null,

  // 實(shí)際進(jìn)行處理的方法
  performProcessing:function(){
    // 實(shí)際執(zhí)行的代碼
  },

  // 初始處理調(diào)用的方法
  process:function(){
    clearTimeout(this.timeoutId);

    var that = this;
    this.timeoutId = setTimeout(function(){
      that.performProcessing();
    },100)
  }
}

這兩個函數(shù)是從JavaScript高級程序設(shè)計(jì)第612-615頁摘出來的铡原,本質(zhì)是不要阻塞了Javascript的事件循環(huán),把任務(wù)分片了商叹。

做服務(wù)器請求多了燕刻,使用 cluster 模塊

cluster 的方案就是多進(jìn)程方案。cluster 能包證每個請求被一個 nodejs 實(shí)例處理剖笙。這樣就能減少每個 nodejs 的處理的數(shù)據(jù)量卵洗。

7 總結(jié)

從現(xiàn)在來看 nodejs 架構(gòu)中對 js 引擎不支持線程調(diào)用是一個較大的遺憾,意味著在 nodejs 中你甚至不能做一個很大的計(jì)算量的事弥咪。不過又說回來过蹂,這也是一件好事。因?yàn)檫@樣做的聚至,使 javascript 變簡單酷勺,寫 js 不需要考慮鎖的事情,想想在 java 中集合類加鎖扳躬,你還要考慮同步脆诉,你還要考慮死鎖勋功,我覺得寫 js 的人都很幸福。

其他語言

同樣的問題也出現(xiàn)在 python库说、ruby 和 php 上。這些語言在當(dāng)前的主流版本(用c實(shí)現(xiàn)的版本)中都默認(rèn)一把大鎖 GIL片择,所有的代碼都是主線程中運(yùn)行潜的,代碼都是線程安全的,基本上第三方庫也利用這個現(xiàn)實(shí)字管。導(dǎo)致的事實(shí)是它們都沒有辦法很好的利用現(xiàn)在的多核計(jì)算機(jī)啰挪,多么悲劇的事情啊嘲叔!

不過好在亡呵,計(jì)算這事情,它們干不了硫戈,還有人來干锰什,就是老大哥 c、c++還有 java 了丁逝。你沒有看到分布式計(jì)算領(lǐng)域和大數(shù)據(jù)中核心計(jì)算被老大哥占領(lǐng)汁胆,其他是想占也占不了,不是不想占霜幼,是有心無力嫩码。

就目前的分析,我覺得這篇文章說的很對罪既。

未來發(fā)展

當(dāng)前 nodejs 的發(fā)展還是在填別的語言中經(jīng)歷過的坑铸题,因?yàn)?nodejs 發(fā)展畢竟才七年的時間(2009年建立),流行也才是近幾年的事情琢感。不過 nodejs 的進(jìn)步很快(后發(fā)優(yōu)勢)丢间,做一個輕量級的網(wǎng)頁應(yīng)用已經(jīng)是繼 python、ruby猩谊、php之后的另一個選擇了千劈,可喜可賀。

但是如果還要更近一步發(fā)展牌捷,那就必須解決計(jì)算這個問題墙牌。當(dāng)前 javascript 對于這個問題的解決基本還是按著沿用 python、ruby 和 php 走過的路線走下去暗甥,采用單線程協(xié)程的方案喜滨,也就是 yieldasync/wait 方案撤防。在這之后虽风,也基本上會采用多線程方案 worker 。從這樣的發(fā)展來看,未來的 nodejs 與 python辜膝、ruby无牵、php 是并駕齊驅(qū)的解決方案,不見得比 python茎毁、ruby 和 php 更好,它們都差不多忱辅,唯一不同的是我們又多了一種選擇而已七蜘。

想到程序員在論壇上問:新手學(xué)習(xí)網(wǎng)站開發(fā),javacript墙懂、python橡卤、ruby和 php 哪個好?我想說如果有師博他說什么好就學(xué)什么损搬,如果沒有師博那就學(xué) javascript 吧碧库,因?yàn)槟悴挥迷偃W(xué)一門后端的語言了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巧勤,一起剝皮案震驚了整個濱河市谈为,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌踢关,老刑警劉巖伞鲫,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異签舞,居然都是意外死亡秕脓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門儒搭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吠架,“玉大人,你說我怎么就攤上這事搂鲫“” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵魂仍,是天一觀的道長拐辽。 經(jīng)常有香客問我,道長擦酌,這世上最難降的妖魔是什么俱诸? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮赊舶,結(jié)果婚禮上睁搭,老公的妹妹穿的比我還像新娘赶诊。我一直安慰自己,他們只是感情好园骆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布舔痪。 她就那樣靜靜地躺著,像睡著了一般锌唾。 火紅的嫁衣襯著肌膚如雪辙喂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天鸠珠,我揣著相機(jī)與錄音,去河邊找鬼秋麸。 笑死渐排,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灸蟆。 我是一名探鬼主播驯耻,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼炒考!你這毒婦竟也來了可缚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤斋枢,失蹤者是張志新(化名)和其女友劉穎帘靡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓤帚,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡描姚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了戈次。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轩勘。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怯邪,靈堂內(nèi)的尸體忽然破棺而出绊寻,到底是詐尸還是另有隱情,我是刑警寧澤悬秉,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布澄步,位于F島的核電站,受9級特大地震影響和泌,放射性物質(zhì)發(fā)生泄漏驮俗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一允跑、第九天 我趴在偏房一處隱蔽的房頂上張望王凑。 院中可真熱鬧搪柑,春花似錦、人聲如沸索烹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽百姓。三九已至渊额,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間垒拢,已是汗流浹背旬迹。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留求类,地道東北人奔垦。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像尸疆,于是被迫代替她去往敵國和親椿猎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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

  • topics: 1.The Node.js philosophy 2.The reactor pattern 3....
    宮若石閱讀 1,057評論 0 1
  • Node.js是目前非呈偃酰火熱的技術(shù)犯眠,但是它的誕生經(jīng)歷卻很奇特。 眾所周知症革,在Netscape設(shè)計(jì)出JavaScri...
    w_zhuan閱讀 3,607評論 2 41
  • 看到很多文件介紹關(guān)于Node.js中的事件循環(huán)筐咧,但是總是有些地方不是很理解,最近無意中看到了Node官方文檔中對事...
    勤勞的小葉閱讀 3,245評論 0 5
  • JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop 作者:阮一峰 日期:2014年10月 8日 一年前噪矛,...
    猩崽大叔閱讀 704評論 0 4
  • 今天的話題嗜浮,讓人能感覺到危機(jī)。就象從舒適區(qū)來到了不安全區(qū)域一樣摩疑。每天都需要輸出危融,不輸出自己就會有損失(金錢和成長)...
    梅啟林閱讀 187評論 2 2