JavaScript 中的非同步程式設計
一、回撥函式
為了實現JS中的非同步操作,主要的解決方案就是利用回撥函式的方法
回撥函式是先呼叫,後定義的方式使用的。
如:
function d1(interval,callback){
setTimeout(()=>{
callback('hello world')
},interval)
}
d1(1000,data=>{
console.log(data)
})
二、Promise
Promise
是ES6封裝的物件,Promise
物件可以化解回撥地獄callback hell
的情況。
Promise
物件代表一個非同步操作,有三種狀態:pending
fulfilled
(已成功)和rejected
(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。
一般用resolve
來表示fulfilled
1.建構函式
new Promise((resolve,reject)=>{
非同步方法
})
注意:
- 建構函式中的函式引數會立即開始執行。也就是說,構造Promise時,非同步方法就會開始執行。
- resolve,reject是兩個回撥方法
2.then
最重要的API(then)。then是當建構函式中非同步方法執行完畢後,才會執行的方法。then中的第一個引數就是建構函式中resolve的回撥,第二個引數就是建構函式中的reject的回撥:
then((data)=>{
},(err)=>{
})
3.then的鏈式呼叫
then可以鏈式呼叫,並且可以解決回撥地域的問題。如果then中resolve的回撥中有retuen Promise1物件,則當前then執行後返回一個Promise1物件。如:
封裝一個fs模組下的readFileAPI:
let pFileR=(FilePath)=>{
return new Promise((resolve,reject)=>{
fs.readFile(FilePath,'utf8',(err,data)=>{
if (err){
reject(err)
}
resolve(data)
})
})
}
pFileR('./a.txt')
.then((data)=>{
console.log(data)
return pFileR('./b.txt')
})
.then((data)=>{
console.log(data)
return pFileR('./c.txt')
})
.then((data)=>{
console.log(data)
})
三、async函式
3.1 async函式使用環境
async
函式是為了實現對非同步操作的控制而產生的。本身和Generator
函式沒有區別,實際上就是後者的語法糖。async
函式配合await來代替Gennerator
函式的*
和yield
。如果不瞭解Gennerator
函式的話沒有關係。直接從更好用的async
開始學習。- 以前對於非同步操作的繼發執行(比如:非同步A執行完之後在執行非同步B)都是使用函式巢狀的方式,後來上文中介紹的
Promise
物件,現在有了ES7
中正式提出的async
函式來控制非同步函式。
3.2 async函式使用方式
-
在
function
前加入async
宣告該方法為async
函式 -
在函式體中,在非同步操作前加入
await
用以宣告,一般該非同步操作應當為Promise
物件化的 -
在一個
await
聲明後的非同步操作時,整個async
函式將會暫停向下執行,直到當前非同步操作執行完畢後才會向下執行,但是當該非同步操作出錯時,async
函式將會結束整個函式,並返回一個reject(err)
的Promise
物件。如果要實現,當異步出錯時,也繼續向下執行async
函式的話,需要配合try..catch
或.catch()
使用。 -
async
返回一個預設的promise
物件,如果return
一個promise
物件的話則返回該Promise
物件,如果return
一個其他資料的的話則返回一個Promise
物件,且該物件的resolve
的引數為當時return
的資料
3.3 async函式實現非同步繼發執行
function sleep(interval) {
return new Promise(resolve => {
setTimeout(resolve, interval);
})
}
// 用法
async function one2FiveInAsync() {
for(let i = 1; i <= 5; i++) {
await sleep(1000); //實際上就是暫停one2FiveInAsync函式1s後再繼續執行
console.log(i);
}
}
one2FiveInAsync()
/*
result:
after 1s : 1
....
after 5s : 5
*/
3.4 實現異步出錯處理
3.4.1 使用try-catch捕獲異常
在下面這個例子中,放在同一個try中的當遇到一個await定義的非同步後將被catch捕獲異常,然後跳出try,繼續向下執行,當遇到沒有在try中的await定義的異步出錯時,退出了整個async函式
//try..catch方式
async function a(){
try{
await Promise.reject('出錯啦1')
await Promise.reject('出錯啦2')
}catch(e){
console.log(e)
}
await Promise.reject('出錯啦3')
await Promise.resolve('成功啦')
}
a().then(d=>{console.log(d)})
.catch(e=>{console.log(e)})
/*
result:
出錯啦1
出錯啦3
*/
3.4.2 使用catch方法捕獲異常
async function a(){
await Promise.reject('出錯啦!1').catch(e=>{console.log(e)})
await Promise.reject('出錯啦!2').catch(e=>{console.log(e)})
await Promise.resolve('成功啦')
}
a()
.then(d=>{console.log(d)})
.catch(e=>{console.log(e)})
/*
result:
出錯啦!1
出錯啦!2
undefined
*/
3.5 實現非同步的類併發執行
類併發執行,即非繼發執行。在async中實現類併發,可以配合Promise.all方法使用
await Promise.all([Example1(),Example2()])