我瞭解到的node異步編程可分紅:
1.回調函數
2.pub/sub模式(發佈/訂閱模式)
3.promise
4.generator
5.async awaitjavascript
該方法是最直接經常使用的異步操做方式,好比在setInterval 和 ajax等會使用到, 存在缺點有:
1.代碼不易閱讀而且容易出現金字塔嵌套問題;
2.通常只能對應一個回調函數(一對一),使用上有必定的限制;java
fs.readFile('/etc/passwd', function (err, data) { if (err) throw err; console.log(data); });
函數先讀取/etc/passwd,再執行回調函數,在這二者之間拋出的錯誤會以參數形式傳入回調函數中。node
該方法再也不侷限於一對一的形式,以多對多的形式監聽事件,能夠很方便的訂閱和取消訂閱存在缺點有:須要藉助類庫(jQuery),事件與回調函數的順序很重要。es6
回調函數的缺點之一是容易出現函數多層嵌套,難以維護的場面.而es6語法中的promise正好解決這類問題. Promise包含三種狀態:pending、fulfilled、rejected,三種狀態只能發生兩種轉換(從pending—>fulfilled、pending—>rejected),而且狀態的轉換僅能發生一次。
Promise實例生成之後,能夠用then方法分別指定Resolved狀態和Reject狀態的回調函數:
then方法能夠接受兩個回調函數做爲參數。第一個回調函數是Promise對象的狀態變爲Resolved時調用,第二個回調函數是Promise對象的狀態變爲Reject時調用。
a) then方法返回Promise。這樣就實現了多個異步操做的串行操做。
b)實現了多個不一樣異步庫之間的轉換。ajax
var p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) var p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2.then(result => console.log(result)) p2.catch(error => console.log(error)) // Error: fail
Promise.all方法用於將多個Promise實例,包裝成一個新的Promise實例。是咱們項目中正在使用的方式。如p的狀態由p一、p二、p3決定,分紅兩種狀況。var p = Promise.all([p1,p2,p3]);
(1)只有p一、p二、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。編程
(2)只要p一、p二、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。數組
Generator最大的特色就是能夠實現函數的暫停、重啓,這個特性很是有利於解決異步操做有兩個名詞須要注意yield 和 next。promise
function* gen(x){ try { var y = yield x + 2; } catch (e){ console.log(e); } return y; } var g = gen(1); g.next(); g.throw('出錯了'); // 出錯了
除此以外,generator函數還有兩個小幫手Thunk函數(自動控制Generator函數的流程,接收和交還程序的執行權的一種機制)和co模塊(用於Generator函數的自動執行。)
ES7提供了async函數,成爲generator函數的語法糖,它主要就是用async來講代替*,用await來代替yield,除此以外,他還帶來了一些便利之處:
1.generator函數的調用須要藉助next方法或者是co模塊,而async和普通函數的調用同樣,不須要藉助任何函數;
2.co模塊約定,yield命令後面只能是Thunk函數或Promise對象,而async函數的await命令後面,能夠是Promise對象和原始類型的值
注意的是: await 命令只能用在 async 函數之中,若是用在普通函數,就會報錯。併發
async function dbFuc(db) { let docs = [{}, {}, {}]; // 報錯 docs.forEach(function (doc) { await db.post(doc); }); }
上面代碼會報錯,由於 await 用在普通函數之中了。可是,若是將 forEach 方法的參數改爲 async 函數,也有問題。異步
async function dbFuc(db) { let docs = [{}, {}, {}]; // 可能獲得錯誤結果 docs.forEach(async function (doc) { await db.post(doc); }); }
上面代碼可能不會正常工做,緣由是這時三個 db.post 操做將是併發執行,也就是同時執行,而不是繼發執行。正確的寫法是採用 for 循環。
async function dbFuc(db) { let docs = [{}, {}, {}]; for (let doc of docs) { await db.post(doc); } }
若是確實但願多個請求併發執行,可使用 Promise.all 方法。
async function dbFuc(db) { let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = await Promise.all(promises); console.log(results); } // 或者使用下面的寫法 async function dbFuc(db) { let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = []; for (let promise of promises) { results.push(await promise); } console.log(results); }
內容涉及的不是很深刻,後續會繼續補充和修改,但願你們多多指正