node.js 12 異步 async

上一篇:


node.js 12 異步 async/await

node.js


在我的node.js系列的開篇,有介紹過node.js具有單線程,異步,非阻塞的特點。如何理解這些特點?舉一個例子,如果遇到耗時操作,比如網絡交互或者磁盤IO,是不是需要等待操作結束再執行下一步操作?

對於node.js來講,屬於單線程,如果需要等待操作結束再執行後面的操作就麻煩了。

node.js選擇的方式是在發起一個調用後不等待結果,繼續向下執行。node.js這裡採用的機制是異步+回調,通過異步和回調來實現對高併發的支持

回調函數

將一個函數作為參數傳遞給另一個函數,並且作為參數的函數可以被執行,其本質上是一個高階函數。

我們用之前介紹過的文件模塊中的函數舉例。

例如,在執行讀文件操作時,可以使用readFile方法。這個方法就是用了回調函數。

<code>fs.readFile('./test.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
console.log(data.toString())
})/<code>

嵌套回調

如果我們寫代碼需要讀取三個文件,按照以前的方式,我們無法知道哪個文件的讀取先結束。也就是說,如果我們需要先讀取A文件,再讀取B文件,只能進行回調函數的嵌套調用。

<code>//先讀取A
fs.readFile('./A.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
//再讀取B
fs.readFile('./A.txt', (err, data)=>{
}
})/<code>

如果需要讀取多個文件並有明確的順序要求,這個代碼的可讀性就很糟糕了。

在文件模塊裡,node.js還提供了另一個方法fs.readFileSync()。這是一個同步函數,可以直接得到結果。

但在業務邏輯中,面對大量的回調函數,如何進行操作呢?

使用Promise

Promise是對異步操作的封裝,提供了三個狀態。

  • 操作在執行中: Pending
  • 操作成功: Resolved
  • 操作失敗: Rejected

從上面可以看出,pending是一箇中間狀態,一旦一個異步操作執行完成以後,或者轉換為Resolved,或者轉換為Rejected。

要使用Promise,首先需要用Promise的構造函數來封裝一個現有的異步操作。我們以fs.readFile為例。

<code>function readFilePromise(path){
//初始化Promise
return new Promise(function(resolve, reject){
fs.readFile(path, (err, data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
\t}
})
}/<code>

在回調函數中,需要將不同的返回結果傳入resolve或者reject中。上例中,我們將error傳入reject,表示讀取文件出錯,將data傳入resolve,表示操作成功。

對於上面封裝後的Promise,調用的時候可以通過then()來獲取異步操作的值,即resolve的值。通過catch()方法來reject中的錯誤。

<code>promise
.then(function(data){
//成功
}).catch(function(data){
//出錯
});/<code>

回到之前的問題,如果需要按順序讀取三個文件A,B,C。那麼此時調用前面的promise,就可以promise鏈式調用

<code>readFilePromise("./A.txt").then(function(data){
console.log(data);
return readFilePromise("./B.txt");
}).then(function(data){
console.log(data);
return readFilePromise("./C.txt");
}).then(function(data){
console.log(data);
})/<code>

看起來還是有點繞,有沒有辦法還是讓代碼實際異步執行,但程序看上去和同步一樣呢?

async/await

這就是現在使用起來最為方便的async/await。程序在使用了async/await後看上去可以和同步的代碼一樣,可讀性很強。

還是以剛才順序讀取三個文件為例。

<code>const fs = require('fs');
async function readData(fpath){
//順序讀取三個文件
let fa = await fs.readFile('./A.txt');
let fb = await fs.readFile('./A.txt');

let fc = await fs.readFile('./A.txt');
}
readData(fpath);/<code>

上面簡潔的代碼可以順序讀取三個文件A,B,C。最後只需要直接調用即可。

需要注意的是,在使用async/await時,函數前面一定要寫async,在函數體中,對於異步函數的調用,一定要寫await。在函數外調用該函數時,直接寫函數名即可。

總結

從回調函數,到Promise,再到async/await,顯示了node.js異步操作的不斷演化。在async/await之前還有一個過度方案generator。目前實際使用中,async/await居多。

後面會介紹node.js體系裡的另一個web框架Koa,和express不一樣的地方其中就有異步的處理。Koa就是使用了async/await。

如果有什麼問題,歡迎大家留言討論。


分享到:


相關文章: