js 非同步程式設計

前端攻城小牛啊發表於2018-10-31

一提到非同步程式設計大家首先的想到的一定是回撥函式,這也是最常用的非同步程式設計的形式,但其實常用的還有Promise和Async函式,接下來就讓我們一起學習這幾種常用的非同步程式設計方法。

回撥函式

js 非同步程式設計

回撥函式就是把任務的第二段單獨寫在一個函式裡面,等到重新執行這個任務的時候,就直接呼叫這個函式,來看一個簡單的例子:

function print(name, callback) {
 setTimeout(() => {
 console.log(name)
 if (callback) {
 callback()
 }
 }, 1000)
}
print('a', function () {
 print('b')
})
複製程式碼

上面這個例子中將print('b')放在print('a')的回撥函式中,這樣就能按順序依次列印a、b,但是回撥函式有一個很明顯的問題,就是當回撥函式巢狀過深時,會導致程式碼混亂,不夠清晰,這就是人們常說的對調地獄,來看下面這個例子:

function print(name, callback) {
 setTimeout(() => {
 console.log(name)
 if (callback) {
 callback()
 }
 }, 1000)
}
print('a', function () {
 print('b', function () {
 print('c', function () {
 print('d')
 })
 })
})
複製程式碼

當我們想按順序依次列印a、b、c、d時,程式碼就變成上面的樣子,可以看到,我們的程式碼形成四層巢狀,如果還要加回撥函式就要繼續巢狀,這樣巢狀會越寫越深,越來越難以維護,此時我們就必須考慮用新的技術去改進,es6的Promise函式應運而生,接下來讓我們看Promise函式是如何改進這個問題的。

Promise

function print(name) {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 console.log(name)
 resolve()
 }, 1000)
 })
}
print('a').then(() => {
 return print('b')
})
 .then(() => {
 return print('c')
 })
 .then(() => {
 return print('d')
 })
複製程式碼

和之前用回撥函式的形式相比,Promise函式寫法更加清晰,由回撥函式的巢狀呼叫變成了鏈式呼叫,但是Promise也有一個很嚴重的問題就是程式碼冗餘,原來的任務被Promise包裝了一下,不管什麼操作都是放在then函式裡面,導致程式碼的語以變差,有什麼更好的解決辦法呢?如果您對Promise函式還想有更深入的瞭解,可以去看阮一峰老師es6入門

Async

在正式使用非同步函式之前,先簡單的介紹一下它的用法,async通常與await一起使用,async函式返回一個Promise物件,可以使用then方法新增回撥函式。當函式執行的時候,一旦遇到await就會先返回,等到觸發的非同步操作完成,再接著執行函式體後面的語句。做了簡單的介紹後,接下來,我們來async函式是怎麼對Promise呼叫優化的。看下面的例子:

function print(name) {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 console.log(name)
 resolve()
 }, 1000)
 })
}
async function test () {
 await print('a')
 await print('b')
 await print('c')
 await print('d')
}
test()
複製程式碼

async函式來處理之前的問題,程式碼就是上面的這個例子中所展示的樣子,是不是感覺程式碼瞬間清晰了,而且程式碼更加好理解了,再仔細思考一下使用async非同步函式就很完美了嗎?其實async非同步函式也有其固有的問題,接下來我們就看看async非同步函式還有什麼問題需要解決。

錯誤捕獲

非同步函式第一個需要解決的問題就是錯誤捕獲的問題,讓我們看看一般情況下async非同步函式是怎麼做錯誤捕獲的,來看一個例子:

function print(name) {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 console.log(name)
 resolve()
 }, 1000)
 })
}
async function test () {
 try {
 await print('a')
 } catch (err) {
 console.log(err)
 }
}
test()
複製程式碼

當使用上述形式的try,catch進行錯誤捕獲的時候,是不是覺得程式碼和使用Promise函式時一樣囉嗦,那有沒有好的解決辦法呢?讓我們來看另外一個例子:

function print(name) {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 console.log(name)
 resolve('a')
 }, 1000)
 })
}
async function test () {
 let [ err, result ] = await to(print('a'))
 if (err) throw err
 return result
}
test()
複製程式碼

to.js:

function to(promise, errorExt) {
 return promise
 .then(function (data) { return [null, data]; })
 .catch(function (err) {
 if (errorExt) {
 Object.assign(err, errorExt);
 }
 return [err, undefined];
 });
}
export { to };
export default to; //歡迎加入全棧開發交流群一起學習交流:864305860
複製程式碼

上述例子中,將async非同步函式的錯誤處理封裝到了一個to.js中,這裡面其實只有一個簡單方法,傳入一個Promise物件,對Promise物件進行錯誤捕獲返回值,用解構的形式獲取返回值和錯誤,這樣就不需要反覆寫try catche做錯誤捕獲了。to.js是一個開源庫

非同步陷阱

什麼是非同步陷阱呢?在使用async非同步函式的時候,多個非同步操作是可以同時執行,但是有await命令變成了繼發的形式了,即必須等待前一個執行完了後一個才能執行,還是之前的例子:

function print(name) {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 console.log(name)
 resolve()
 }, 1000)
 })//歡迎加入全棧開發交流群一起學習交流:864305860
}
async function test () {
 await print('a')
 await print('b')
 await print('c')
 await print('d')
}
test()
複製程式碼

假設await print('a')、await print('b')、await print('c')、await print('d')這四個操作並沒有先後的邏輯關係,可以同時執行,那麼按照上面的寫法就會導致前一個執行完再執行下一個,整個執行過程中的等待時間會有4s,但是同時執行的等待時間就只有1s,這是在使用async非同步函式會經常忽略的一個問題,那麼怎麼解決呢?介紹一個我經常使用的辦法,看例子:

function print(name) {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 console.log(name)
 resolve('a')
 }, 1000)
 })
}
async function test () {
 Promise.all([print('a'), print('b'), print('c'), print('d')])
}
test()
//歡迎加入全棧開發交流群一起學習交流:864305860
複製程式碼

其實解決辦法很簡單就是通過Promise.all()方法,將所有非同步操作作為引數陣列傳入,這樣print('a')、print('b')、print('c')、print('d')這四個非同步操作就可以併發執行了。

總結

這篇文章簡單的介紹了一些常用的非同步程式設計的方法,如果有錯誤或不嚴謹的地方,歡迎批評指正,如果喜歡,歡迎點贊收藏。

本次給大家推薦一個免費的學習群,裡面概括移動應用網站開發,css,html,webpack,vue node angular以及面試資源等。 對web開發技術感興趣的同學,歡迎加入Q群:864305860,不管你是小白還是大牛我都歡迎,還有大牛整理的一套高效率學習路線和教程與您免費分享,同時每天更新視訊資料。 最後,祝大家早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峰。

相關文章