node核心:異步流程控制

Node.js的異步是整個學習Node.js過程當中重中之重。前端

1)異步流程控制學習重點vue

2)Api寫法:Error-first Callback 和 EventEmitternode

3)中流砥柱:Promisejquery

4)終極解決方案:Async/Awaitgit

node.js必知必會

  1. .Node.js SDK裏callback寫法必須會的。github

  2. Node.js學習重點: Async函數與Promisenpm

    1.中流砥柱:Promiseapi

    2.終極解決方案:Async/Awaitpromise

1. Api 寫法:Error-first Callback 和EventEmitter

1.1 Error-first Callback

定義錯誤有限的回調寫法只須要注意2條規則便可:併發

  1. 回調函數的第一個參數返回的error對象,若是error發生了,它會做爲第一個參數返回,若是沒有,通常作法是返回null.
  2. 回調函數的第二個參數返回的是任何成功響應的結果數據。若是結果正常沒有error發生,err會被設置爲null,並在第二個參數就出返回成功結果數據。

下面讓咱們看一下調用函數示例,Node.js文檔例最常採用下面這樣的回調方式:

function(err,res){
    //process the error and result
}

這裏的 callback 指的是帶有2個參數的函數:「err」和「res」 。語義上講,非空的"err"至關於程序異常;而空的「err」至關於能夠正常返回結果「res」,無任何異常。

1.2 EventEmitter

事件模塊是 Node.js內置的對觀察者模式「發佈/訂閱」(publish/subscribe)的實現,經過 EventEmitter屬性,提供了一個構造函數。該構造函數的實例具備 on 方法,能夠用來監聽指定事件,並觸發回調函數。任意對象均可以發佈指定事件,被 EventEmitter 實例的 on方法監聽到。

在node6以後,能夠直接使用require('events')類

var EventEmitter = require('events')
var util = require('util')
var MyEmitter = function(){
    
}
util.inherits(MyEmitter,EventEmitter)

const myEmitter = new MyEmitter();

myEmitter.on('event',(a,b)=>{
 console.log(a,b,this)
 // Prints: a b {}
})
myEmitter.emit('event','a','b')

和jquery、vue 裏的Event 是很是相似的。並且前端本身也有EventEmitter。

1.3 如何更好的查Node.js文檔

API 是應用程序接口 Application Programming interface的簡稱。從Node.js異步原理,咱們能夠知道,核心在於Node.js SDK中API調用,而後交由EventLoop(Libuv)去執行,因此咱們必定要熟悉Node.js的API操做。

Node.js的API都是異步的,同步的函數是奢求,要查API文檔,在高併發場景下慎用。

筆者推薦使用 DeshZeal查看離線文檔,對api理解會深刻不少,比IDE輔助要好,能夠有效避免離開IDE就不會寫代碼的窘境。

image

1.4 中流砥柱:Promise

回調地獄

Node.js由於採用了錯誤優先的回調風格寫法,致使sdk裏導出都是回調函數,就會特別痛苦,常常會出現回調裏嵌套回調的問題,你們都很是厭煩這種寫法,稱之爲 Callback Hell,即回調地獄。一個經典的例子來自著名的Promise模塊 q 文檔裏。

step1(function(value1){
   step2(value1,function(value2){
       step3(value2,function(value3){
           step4(value3,function(){
               // Do something with value4
           });
       });
   });
});

這裏只是作4步,嵌套了4層回調,若是更多步驟呢?不少新手淺嘗輒止,到這兒就望而卻步,粉轉黑。這明顯不夠成熟,起碼你要看看它的對應解決方案吧!

Promise最先也是在commonjs社區提出來的,當時提出了不少規範。比較接受的是promise/A規範。後來人們在這個基礎上,提出了promise/A+規範,也就是實際上如今的業內推行的規範。ES6 也是採用的這種規範。

Promise意味着[許願|承諾]一個尚未完成的操做,但在將來會完成的。與Promise最主要的交互方法是經過將函數傳入它的then方法從而獲取得Promise最終的值或Promise最終拒絕(reject)的緣由。要點有三個:

  1. 遞歸,每一個異步操做返回的都是promise對象
  2. 狀態機: 三種狀態轉換,只在promise對象內部能夠控制,外部不能改變狀態
  3. 全局異常處理
定義
var promise = new Promise(function(resolve, reject) {
    //do a thing,possibly async, then...
    if (/*everything turned out fine*/) {
        resolve("Stuff worked!")
    }
    else {
        reject(Error("It broke"))
    }
})

每一個Promise定義都是同樣的,在構造函數裏傳入一個匿名函數,參數是resolve和reject,分別表明成功和失敗時候的處理。

調用
promise.then(function(text){
    console.log(text)//Stuff worked!
    return Promise.reject(new Error('我是故意的'))
}).catch(function(err){
    console.log(err)
})

它的主要交互方式是經過then函數,若是Promise成功執行resolve了,那麼它就會將resolve的值傳給最近的then函數,做爲它的then函數的參數。若是出錯reject,那就交給catch來捕獲異常就行了。

Promise 的最大優點是標準化,各種異步工具庫都按照統一規範實現,即便是async函數也能夠無縫集成。因此用 Promise 封裝 API 通用性強,用起來簡單,學習成本低。在async函數普及以前,絕大部分應用都是採用Promise來作異步流程控制的,因此掌握Promise是Node.js學習過程當中必需要掌握的重中之重。

Bluebird是 Node.js 世界裏性能最好的Promise/a+規範的實現模塊,Api很是齊全,功能強大,是原生Promise外的不二選擇。

好處以下:

  1. 避免Node.js內置Promise實現 問題,使用與全部版本兼容
  2. 避免Node.js 4曾經出現的內存泄露問題
  3. 內置更多擴展,timeout、 promisifyAll等,對Promise/A+規範提供了強有力的補充

在學習Node.js過程當中,對於Promise瞭解多深刻都不過度。

推薦學習資料

  1. Node.js最新技術棧之Promise篇 https://cnodejs.org/topic/560dbc826a1ed28204a1e7de
  2. 理解 Promise 的工做原理 https://cnodejs.org/topic/569c8226adf526da2aeb23fd
  3. Promise 迷你書 http://liubin.github.io/promises-book/

1.4 終極解決方案:Async/Await

Async/Await是異步操做的終
極解決方案,Koa 2在node 7.6發佈以後,立馬發佈了正式版本,而且推薦使用async函數來編寫Koa中間件。

這裏給出一段Koa2應用裏的一段代碼:

exports.list = async(ctx, next)=>{
    try {
        let students = await Student.getAllAsync();
        
        await ctx.render('students/index',{
            students : students
        })
    } catch (err) {
        return ctx.api_error(err)
    }
}

它作了3件事兒

  1. 經過await Student.getAllAsync();來獲取全部的students信息。
  2. 經過await ctx.render渲染頁面
  3. 因爲是同步代碼,使用try/catch作的異常處理

是否是很是簡單,如今Eggjs裏也都是這樣同步的代碼。

(1)正常寫法
const pkgConf = require('pkg-conf')
async function main(){
    const config = await pkgConf('unicorn');
    console.log(config.ranbow);
    //true
}
main()

變態寫法:

const pkgConf = require('pkg-conf');
(async ()=> {
    const config = await pkgConf('unicorn')
    
    console.log(config.ranbow)
    //true
})()
(2)await + Promise
const Promise = require('bluedbird');
const fs =  Promise.promisifyAll(require("fs"));

async function main(){
    const contents = await fs.readFileAsync("myfile.js","utf8")
    console.log(contents)
}

main()
(2)await + co + generator
const co = require('co')
const Promise = require('bluedbird')
const fs = Promise.promisifyAll(require("fs"))

async function mian(){
    const contents = co(function* () {
        var result = yield fs.readFileAsync("myfile.js","utf8")
        return result;
    })
    console.log(contents)
    
}
main()

要點:

  • co 的返回值是promise,因此await 能夠直接接co.
  • co 的參數是generator
  • 在gennerator 裏可使用yield,而yield後面接的有5種可能,故而把這些能夠yeild接的方式稱爲yieldable,便可以yield接的。
    • Promises
    • Thunks(functions)
    • array(parallel execution)
    • objects (parallel execution)
    • Generators 和 GeneratorFunctions

由上面3種基本用法能夠推出Async 函數要點以下:

  • Async函數語義上很是好
  • Async不須要執行器,它自己具有執行能力,不像Generator須要co模塊
  • Async函數的異常處理採用try/catch和Promise的錯誤處理,很是強大
  • co做爲Generator執行器是不錯的,它更好的是當作Promise 包裝器,經過Generator支持yieldable,最後返回Promise,是否是有點無恥?
小結:

這部分共講了4個小點,都是極其直接的必須掌握的知識點。

  • 異步流程控制學習重點
    • 2)Api寫法:Error-first Callback 和 EventEmitter
    • 3)中流砥柱:Promise
    • 4)終極解決方案:Async/Await
    • 這裏再提一下關於Node.js源碼閱讀問題,不少人api都還沒完熟練就去閱讀源碼,這是很是不同意的,不帶着問題去讀源碼是比較容易迷失在大量代碼中的。效果並很差。

先用明白,而後再去閱讀Node.js源碼,而後探尋libuv併發機制。不少人買了樸大的《深刻淺出Node.js》一書,看了以後仍是不太會用,不是書寫的很差,而是步驟不對。

  1. Node in action和了不得的Node.js是入門的絕好書籍,很是簡單,各個部分都講了,但不深刻,看了以後,基本就能用起來了
  2. 當你用了一段以後,你會對Node.js的運行機制好奇,爲啥呢?這時候去讀樸大的《深刻淺出Node.js》一書就可以解惑。緣由很簡單,九淺一深一書是偏向底層實現原理的書,從操做系統,併發原理,node源碼層層解讀。若是是新手讀,不免會比較鬱悶。
  3. 實踐類的能夠看看雷宗民(老雷)和趙坤(nswbmw)寫的書
  4. 我通常給你們的推薦是把Node in action讀上5遍10遍,入門幹活足夠了。剩下的就是反覆實踐,多寫代碼和npm模塊就好。

迷茫時學習Node.js最好的方法
Node.js 編寫的包管理器 npm 已成爲開源包管理了領域最好的生態,直接到2017年10月份,有模塊超過47萬,每週下載量超過32億次,每月有超過700萬開發者使用npm。如今早已經超過60萬個模塊了。

這裏就不一一舉例了,給出一個迷茫時學習Node.js最好的方法吧!

"天天看10個npm模塊"

對於學習Node.js迷茫的人來講,這是最好的方式,當你不知道如何作的時候,就要向前(錢)看,你要知道積累哪些技能對之後有好處。對於學習Node.js必經之路,必定是要掌握不少模塊用法,並從中汲取技巧、思路、設計思想的。與其不知道學什麼,爲何不天天積累幾個技巧呢?

推薦一個repo即 https://github.com/parro-it/awesome-micro-npm-packages 小型庫集合,一天看十個不是夢!

更多討論 https://zhuanlan.zhihu.com/p/29625882

此爲學習node的學習筆記,爲了本身學習查閱,轉自狼叔的教程,若是侵權請聯繫我刪除
閱讀原文

相關文章
相關標籤/搜索