什麼是回調?
回調是異步編程的基礎方法,當須要按照順序執行異步邏輯的時候,通常採起後續傳遞的方式,也就是將後續邏輯封裝在回調函數中做爲起始函數的參數,逐層去嵌套。node
function lean(something){ console.log(something) } function we(callback,something){ something+=' is cool' callback(something)//結果傳遞給回調函數 } we(lean,'node')//具名函數 we(function(something){//傳遞匿名函數 console.log(something) },'jade')
什麼是同步
同步就是執行一個任務,後一個任務等待前一個任務結束,而後再執行,程序執行的順序與任務的排列順序是一致的,好比js是單線程的,所以js代碼是按照順序執行的,即使是js同步下載多個文件,也得按照順序執行,一旦js裏面出現死循環的代碼,那麼頁面就會被阻塞在這個地方,後面的js代碼就不會被執行到 好比打電話預定座位,發現沒有座位了,店員就掛電話開始查找有沒有座位,查找,查找...等待一下子以後,店員經過回電話,開始執行回調函數來解決你的問題(是否有座位)。
什麼是異步
異步執行任務,若是發現任務阻塞,js執行setTimeOut,毫秒時間做爲其中的一個參數,而後自動執行另一個函數,不會一直等待。數據庫
//代碼按照順序執行,結果看就是1 var c = 0 function printIt(){ console.log(c) } function plus(){ c+=1 } plus() printIt()
//加上耗時操做,沒有加回調函數,結果不改變 var c = 0 function printIt(){ console.log(c) } function plus(){//加上耗時操做,沒有調用回調函數,因此js一直阻塞在這裏,不能執行,因此結果仍是 0,沒有變化。 setTimeout(function(){ c+=1 },1000) } plus() printIt()
var c = 0 function printIt(){ console.log(c) } function plus(callback){ setTimeout(function(){ c += 1 callback();//5s以後timeout,開始調用回調函數 },5000) } plus(printIt)
什麼是io
磁盤的寫和讀,在nodejs裏面就是爲文件系統,數據庫資源提供接口,發送請求的時候,不用等待硬盤,當硬盤準備好了,非阻塞接口就會通知處理
什麼是阻塞/非阻塞
阻塞,在電話預約座位的時候,你將本身掛起,等待...直到等到有座位的信息的時候才掛電話。
非阻塞,打電話預約座位,沒有等店員回答你是否有結果,你就掛電話了,該幹啥幹啥。你也能夠過幾分鐘過來催一下,看看店員有沒有搞定。
什麼是事件
點擊,拖拽等都是事件
什麼是事件驅動
有事我叫你,沒事別煩我--當須要的時候纔會被調用。
事件循環
主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。
上圖中,主線程運行的時候,產生堆(heap)和棧(stack),棧中的代碼調用各類外部API,它們在"任務隊列"中加入各類事件(click,load,done)。只要棧中的代碼執行完畢,主線程就會去讀取"任務隊列",依次執行那些事件所對應的回調函數。
Node.js的Event Loop
Node.js也是單線程的Event Loop,可是它的運行機制不一樣於瀏覽器環境。
根據上圖,Node.js的運行機制以下。編程
(1)V8引擎解析JavaScript腳本。 (2)解析後的代碼,調用Node API。 (3)libuv庫負責Node API的執行。它將不一樣的任務分配給不一樣的線程,造成一個Event Loop(事件循環),以異步的方式將任務的執行結果返回給V8引擎。 (4)V8引擎再將結果返回給用戶。
除了setTimeout和setInterval這兩個方法,Node.js還提供了另外兩個與"任務隊列"有關的方法:process.nextTick和setImmediate。它們能夠幫助咱們加深對"任務隊列"的理解。瀏覽器
process.nextTick方法能夠在當前"執行棧"的尾部----下一次Event Loop(主線程讀取"任務隊列")以前----觸發回調函數。也就是說,它指定的任務老是發生在全部異步任務以前。setImmediate方法則是在當前"任務隊列"的尾部添加事件,也就是說,它指定的任務老是在下一次Event Loop時執行,這與setTimeout(fn, 0)很像。請看下面的例子(via StackOverflow)。緩存
process.nextTick(function A() { console.log(1); process.nextTick(function B(){console.log(2);}); }); setTimeout(function timeout() { console.log('TIMEOUT FIRED'); }, 0) // 1 // 2 // TIMEOUT FIRED
上面代碼中,因爲process.nextTick方法指定的回調函數,老是在當前"執行棧"的尾部觸發,因此不只函數A比setTimeout指定的回調函數timeout先執行,並且函數B也比timeout先執行。這說明,若是有多個process.nextTick語句(無論它們是否嵌套),將所有在當前"執行棧"執行。異步
如今,再看setImmediate。異步編程
setImmediate(function A() { console.log(1); setImmediate(function B(){console.log(2);}); }); setTimeout(function timeout() { console.log('TIMEOUT FIRED'); }, 0);
上面代碼中,setImmediate與setTimeout(fn,0)各自添加了一個回調函數A和timeout,都是在下一次Event Loop觸發。那麼,哪一個回調函數先執行呢?答案是不肯定。運行結果多是1--TIMEOUT FIRED--2,也多是TIMEOUT FIRED--1--2。函數
使人困惑的是,Node.js文檔中稱,setImmediate指定的回調函數,老是排在setTimeout前面。實際上,這種狀況只發生在遞歸調用的時候。oop
setImmediate(function (){ setImmediate(function A() { console.log(1); setImmediate(function B(){console.log(2);}); }); setTimeout(function timeout() { console.log('TIMEOUT FIRED'); }, 0); }); // 1 // TIMEOUT FIRED // 2
上面代碼中,setImmediate和setTimeout被封裝在一個setImmediate裏面,它的運行結果老是1--TIMEOUT FIRED--2,這時函數A必定在timeout前面觸發。至於2排在TIMEOUT FIRED的後面(即函數B在timeout後面觸發),是由於setImmediate老是將事件註冊到下一輪Event Loop,因此函數A和timeout是在同一輪Loop執行,而函數B在下一輪Loop執行。spa
咱們由此獲得了process.nextTick和setImmediate的一個重要區別:多個process.nextTick語句老是在當前"執行棧"一次執行完,多個setImmediate可能則須要屢次loop才能執行完。事實上,這正是Node.js 10.0版添加setImmediate方法的緣由,不然像下面這樣的遞歸調用process.nextTick,將會沒完沒了,主線程根本不會去讀取"事件隊列"!
process.nextTick(function foo() { process.nextTick(foo); });
事實上,如今要是你寫出遞歸的process.nextTick,Node.js會拋出一個警告,要求你改爲setImmediate。
另外,因爲process.nextTick指定的回調函數是在本次"事件循環"觸發,而setImmediate指定的是在下次"事件循環"觸發,因此很顯然,前者老是比後者發生得早,並且執行效率也高(由於不用檢查"任務隊列")。
Http緩存機制