天天看點

Node.js異步程式設計異步程式設計

異步程式設計

同步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 不會阻塞後面代碼的執行

代碼運作時,同步代碼會放在同步代碼執行區,異步代碼會放在異步代碼執行區和回調函數隊列,等待所有同步代碼完成後,再回調異步代碼執行。

Node.js異步程式設計異步程式設計

但如果需要依次按順序執行三個異步 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 () {}

  1. 普通函數定義前加 async 關鍵字 普通函數變成異步函數
  2. 異步函數預設傳回 promise 對象
  3. 在異步函數内部使用 return 關鍵字進行結果傳回 結果會被包裹的 promise 對象中 return 關鍵字代替了 resolve 方法
  4. 在異步函數内部使用 throw 關鍵字抛出程式異常
  5. 調用異步函數再鍊式調用 then 方法擷取異步函數執行結果
  6. 調用異步函數再鍊式調用 catch 方法擷取異步函數執行的錯誤資訊

await關鍵字

  1. await 關鍵字隻能出現在異步函數中
  2. await promise, await 後面隻能寫 promise 對象 寫其他類型的API是不不可以的
  3. 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();