Node 異步編程
同步方法和異步方法
- 同步方法:立即返回操作結(jié)果,在未執(zhí)行完會阻塞代碼執(zhí)行
- 異步方法:不會立即返回操作結(jié)果炼幔,通過回調(diào)函數(shù)接受結(jié)果,不會阻塞代碼執(zhí)行
異步 API 的執(zhí)行順序
當(dāng)異步函數(shù)執(zhí)行時咳燕,由于執(zhí)行完成的時間不確認(rèn),會將對應(yīng)的回調(diào)函數(shù)壓入事件循環(huán)隊列 , 繼續(xù)執(zhí)行其它代碼萍恕,當(dāng)異步函數(shù)執(zhí)行完成后,才開始處理事件循環(huán)仿畸,調(diào)用對應(yīng)的回調(diào)函數(shù)
異步編程回調(diào)地獄問題
由于異步函數(shù)需要通過回調(diào)函數(shù)來獲取執(zhí)行結(jié)果危尿,當(dāng)我們有多個異步函數(shù)需要執(zhí)行呐萌,并且對執(zhí)行結(jié)果有順序要求,只能將后面的異步函數(shù)寫在前面的異步函數(shù)中谊娇,這樣就造成了回調(diào)地獄問題肺孤,回調(diào)函數(shù)嵌套過深,代碼維護困難
如下面代碼:
// 回調(diào)地獄,
// 需求: 依次讀取 1.txt, 2.txt, 3.txt文件,并且輸出結(jié)果
const fs = require('fs');
fs.readFile('./1.txt', 'utf8', (err, result1) => {
console.log(result1);
fs.readFile('./2.txt', 'utf8', (err, result2) => {
console.log(result2);
fs.readFile('./3.txt', 'utf8', (err, result3) => {
console.log(result3);
});
});
});
Promise 改造回調(diào)地獄代碼
Promise 可以將異步 API 的執(zhí)行和錯誤的處理進行分離济欢,使用 Promise 改造回調(diào)地獄代碼赠堵,如下所示:
// promise 改造回調(diào)地獄代碼
// 需求: 依次讀取 1.txt, 2.txt, 3.txt文件,并且輸出結(jié)果
const fs = require('fs');
function readFile(filePath) {
// 創(chuàng)建 Promise 實列
const promiseObj = new Promise((resolve, reject) => {
// resolve 是一個函數(shù), 如果異步API執(zhí)行成功, 將執(zhí)行結(jié)果傳遞到Promise外部
// reject 是一個函數(shù), 如果異步API執(zhí)行失敗, 將失敗結(jié)果傳遞到Promise外部
// 將異步代碼放到匿名函數(shù)內(nèi)部
fs.readFile(filePath, 'utf8', (error, result) => {
if (error) {
return reject(error)
}
return resolve(result)
})
})
return promiseObj
}
// 依次調(diào)用函數(shù)
let r1 = readFile('1.txt');
let r2 = readFile('2.txt');
let r3 = readFile('3.txt');
// then 成功時候執(zhí)行
// catch 失敗時候執(zhí)行
r1.then(result => {
console.log(result);
// 在 then 內(nèi)部返回下一個 promise對象, 方便使用 鏈?zhǔn)骄幊? return r2
}).then(result => {
console.log(result);
return r3
}).then(result => {
console.log(result);
}).catch(error => {
console.log(error);
})
async 和 await
-
async
- 普通函數(shù)定義前加 async 關(guān)鍵字,普通函數(shù)變成異步函數(shù)
- 異步函數(shù)默認(rèn)返回 promise 對象
- 在異步函數(shù)中使用 return 返回結(jié)果法褥,結(jié)果會被包裹在 promise 對象中茫叭,return 關(guān)鍵字代替了 resolve
- 在異步函數(shù)中使用 throw 拋出程序異常
- 調(diào)用異步函數(shù)后使用 then 獲取異步函數(shù)執(zhí)行的結(jié)果
- 使用 catch 獲取錯誤信息
-
await
- await 關(guān)鍵字只能出現(xiàn)在異步函數(shù)中
- await 后面只能寫 promise 對象,不能寫其他類型的 API
- await 關(guān)鍵字 可以暫停異步函數(shù)向下執(zhí)行挖胃,直到返回執(zhí)行結(jié)果
異步函數(shù)改造回調(diào)地獄代碼
// 異步函數(shù)改造回調(diào)地獄代碼
const fs = require('fs');
// promisify方法改造代碼, 讓異步函數(shù)返回 promise 對象, 以支持異步函數(shù)語法
const promisify = require('util').promisify;
// promisify 改造 fs.readFile 方法
const readFile = promisify(fs.readFile);
// await 只能在異步函數(shù)里面, 所以需要加上 async
async function run(){
// 將 readFile() 返回結(jié)果賦值給 變量, 然后打印
let r1 = await readFile('./1.txt', 'utf8');
let r2 = await readFile('./2.txt', 'utf8');
let r3 = await readFile('./3.txt', 'utf8');
console.log(r1);
console.log(r2);
console.log(r3);
}
run()