異步程式設計
同步API、異步API
- 同步API:隻有目前API執行完畢,才能接着執行下面的API。
console.log('hello');
console.log(' world');
//hello world
- 異步API:目前API的執行不會影響和阻塞後續API的執行。
setTimeout(()=>{
console.log('hello');
},3000);
console.log('world');
// worldhelllo
異步 API 執行結果需要用回調函數擷取
function getMsg (callback) {
setTimeout(function () {
callback ({ msg: 'Hello Node.js' })
}, 2000);
}
getMsg (function (msg) {
console.log(msg);
});
同步API、異步API的執行順序
同步 API 會阻塞後面代碼的執行,需要從上往下依次執行,異步 API 不會阻塞後面代碼的執行
代碼運作時,同步代碼會放在同步代碼執行區,異步代碼會放在異步代碼執行區和回調函數隊列,等待所有同步代碼完成後,再回調異步代碼執行。
但如果需要依次按順序執行三個異步 API 最簡單的方法是不斷的異步 API,但那将會造成異步程式設計的回調地獄問題
例如:需求依次讀取 A 檔案、B 檔案、C 檔案
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)
})
})
});
這樣的代碼将會非常難維護
是以nodejs添加了一個 Promise 對象,目的是解決Node.js異步程式設計中回調地獄的問題。
Promise 對象
Promise 對象用于表示一個異步操作的最終完成 (或失敗),及其結果值。
下面是 promise 對象的一個最基本結構
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (true) {
resolve({name: '張三'})
}else {
reject('失敗了')
}
}, 2000);
});
promise.then(result => console.log(result); // {name: '張三'})
.catch(error => console.log(error); // 失敗了)
其中,resolve 和 reject 是函數帶的兩個參數,其本質也是函數,在執行成功時,會在 promise.then 函數裡接收 resolve 的傳回值作為參數,失敗則在 .catch 函數裡接收 reject 的傳回值作為參數。
程式可以寫為如下
const fs = require('fs');
function p1 () {
return new Promise ((resolve, reject) => {
fs.readFile('./1.txt', 'utf8', (err, result) => {
resolve(result)
})
});
}
function p2 () {
return new Promise ((resolve, reject) => {
fs.readFile('./2.txt', 'utf8', (err, result) => {
resolve(result)
})
});
}
function p3 () {
return new Promise ((resolve, reject) => {
fs.readFile('./3.txt', 'utf8', (err, result) => {
resolve(result)
})
});
}
//把建立的 promise 對象作為函數傳回值
p1().then((r1)=> {
console.log(r1);
return p2();
})
.then((r2)=> {
console.log(r2);
return p3();
})
.then((r3) => {
console.log(r3)
})
異步函數
異步函數是異步程式設計文法的終極解決方案,它可以讓我們将異步代碼寫成同步的形式,讓代碼不再有回調函數嵌套,使代碼變得清晰明了
const fn = async () => {};
async function fn () {}
- 普通函數定義前加 async 關鍵字 普通函數變成異步函數
- 異步函數預設傳回 promise 對象
- 在異步函數内部使用 return 關鍵字進行結果傳回 結果會被包裹的 promise 對象中 return 關鍵字代替了 resolve 方法
- 在異步函數内部使用 throw 關鍵字抛出程式異常
- 調用異步函數再鍊式調用 then 方法擷取異步函數執行結果
- 調用異步函數再鍊式調用 catch 方法擷取異步函數執行的錯誤資訊
await關鍵字
- await 關鍵字隻能出現在異步函數中
- await promise, await 後面隻能寫 promise 對象 寫其他類型的API是不不可以的
- await 關鍵字可是暫停異步函數向下執行 直到 promise 傳回結果
async function p1 () {
return 'p1';
}
async function p2 () {
return 'p2';
}
async function p3 () {
return 'p3';
}
async function run () {
let r1 = await p1()
let r2 = await p2()
let r3 = await p3()
console.log(r1)
console.log(r2)
console.log(r3)
}
run();