用async/await改造Node.js(Express)網(wǎng)站

1.回調(diào)的嵌套陷阱

在Node.js中,使用回調(diào)的方式進(jìn)行異步操作,我們以讀取文件內(nèi)容為例:

const fs = require('fs');

// 定義一個以回調(diào)的方式獲取文件的函數(shù)
function asyncReadFile(path, callback) {
    fs.readFile(path, 'utf-8', function(err, data) {
        callback(err,data)
    })
}

// 調(diào)用:
router.get('/', async function(req, res, next) {
    asyncReadFile("./package.json", (err, data) {
        console.log(data)
        // 渲染頁面
        res.render('index', { title: 'Express' });
    })
})

從上面的調(diào)用不難推測出,以回調(diào)的方式來實(shí)現(xiàn)異步,嵌套將會是開發(fā)者的噩夢:

router.get('/', async function(req, res, next) {
    asyncReadFile("./package.json", (err, data1) {
        console.log(data1)
        asyncReadFile("./app.js", (err, data2) {
            console.log(data2)
            asyncReadFile("./other.js", (err, data3) {
                console.log(data3)
                // ...
                
                // 渲染頁面
                res.render('index', { title: 'Express' });
            })
        })
    })
})

在業(yè)務(wù)查詢比較多、需要同時觸發(fā)的任務(wù)中,回調(diào)會嚴(yán)重影響排版及閱讀,尤其對接手人(甚至開發(fā)者自己)的閱讀和理解造成了很大的困難。

2.使用async/await

接下來,我將改造上面的 asyncReadFile() 函數(shù),以將返回值其構(gòu)造為 Promise 對象。

var fs = require('fs')

var asyncReadFile = function(path) {
    return new Promise(function(resolve, reject) {
        fs.readFile(path, 'utf-8', function(err, data) {
            resolve(data)
        })
    })
}

router.get('/', async function(req, res, next) {
    var file = await asyncReadFile("./package.json")
    console.log(file)
    // 渲染
    res.render('index', { title: 'Express' });
});

這里需要注意的是,asyncawait 是成對出現(xiàn)的。即你要在哪個函數(shù)使用 await 的方式,就應(yīng)當(dāng)對這個函數(shù)進(jìn)行 async 聲明。

事實(shí)上,這個聲明是一種語法糖,即在不改變語法的基礎(chǔ)上,讓代碼的可讀性更好、更不容易出錯。async就是Generator函數(shù)的語法糖。

Generator函數(shù)的用法是這樣的:

var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
};

對應(yīng)的async用法則是:

var gen = async function (){
  var f1 = await readFile('/etc/fstab');
  var f2 = await readFile('/etc/shells');
};

即將 *yield 分別用 asyncawait 替換了。

語法糖(Syntactic sugar),也譯為糖衣語法,是由英國計算機(jī)科學(xué)家彼得·約翰·蘭達(dá)(Peter J. Landin)發(fā)明的一個術(shù)語,指計算機(jī)語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用。通常來說使用語法糖能夠增加程序的可讀性,從而減少程序代碼出錯的機(jī)會。

3.處理reject()

在函數(shù)中添加 reject() 的調(diào)用,并在Promise對象后面追加一個 .catch() 的處理

var fs = require('fs')

var asyncReadFile = function(path) {
    return new Promise(function(resolve, reject) {
        fs.readFile(path, 'utf-8', function(err, data) {
            if(err) {
                reject(err)
            }
            resolve(data)
        })
    })
    // This must be called in case of node-process terminated by reject()
    .catch((err)=>{
        return err
    })
}

router.get('/', async function(req, res, next) {
    var file = await asyncReadFile("./package.json")
    console.log(file)
    // 渲染
    res.render('index', { title: 'Express' });
});

或者在調(diào)用的時候 catch 錯誤

var fs = require('fs')

var asyncReadFile = function(path) {
    return new Promise(function(resolve, reject) {
        fs.readFile(path, 'utf-8', function(err, data) {
            if(err) {
                reject(err)
            }
            resolve(data)
        })
    })
}

router.get('/', async function(req, res, next) {
    try {
        var file = await asyncReadFile("./package.json")
        console.log(file)
    }catch (e) {
        console.error(e);
    }
    // 渲染
    res.render('index', { title: 'Express' });
});

此外,需要注意的是,一旦 reject() 執(zhí)行,后面的代碼就立即停止了。

async function func() {
  await Promise.reject(err);
  await Promise.resolve(); // 不會執(zhí)行
}

4.多個await函數(shù)的并發(fā)

如果由多個await函數(shù)要一起執(zhí)行,且沒有先后關(guān)系,可以讓它們同時執(zhí)行,原型為:

var [f1, f2, ...] = await Promise.all([func1, func2, ...])
var [f1, f2] = await Promise.all([
    asyncReadFile("./app.js"),
    asyncReadFile("./package.json")
])

結(jié)果集中 f1asyncReadFile("./app.js") 的返回值, f2asyncReadFile("./package.js") 的返回值。


參考資料:
《ECMAScript6筆記:異步操作和Async函數(shù)》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 異步編程對JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程,根本...
    呼呼哥閱讀 7,400評論 5 22
  • async 函數(shù) 含義 ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù),使得異步操作變得更加方便。 async 函數(shù)是...
    huilegezai閱讀 1,313評論 0 6
  • 含義 async函數(shù)是Generator函數(shù)的語法糖,它使得異步操作變得更加方便。 寫成async函數(shù),就是下面這...
    oWSQo閱讀 2,040評論 0 2
  • 原文連接:https://blog.csdn.net/sinat_17775997/article/details...
    小豆soybean閱讀 4,361評論 0 7
  • 好幾天沒有寫東西了。今天找了新地方,打算搬家。 對親愛的感覺,沒有那么強(qiáng)烈了。也許現(xiàn)在搬家改變是最好的選擇。給自己...
    孫有騰閱讀 238評論 0 1

友情鏈接更多精彩內(nèi)容