淺談js中的回調地獄問題
-
什麼是回調地獄
- 說起回調地獄 首先想到的是
異步
在js中我們經常會大量使用異步回調,例如使用ajax
請求
我們來看下面這段代碼:
我們發現上面代碼大量使用了回調函數(將一個函數作爲參數傳遞給另個函數)並且有許多function a(functionb(){ c(function d(){ }) })
})
結尾的符號,使得代碼看起來很混亂。
- 說起回調地獄 首先想到的是
-
如何解決回調地獄呢
-
第一種使用ES6中的
Promise
,中文翻譯過來承諾
,意思是在未來某一個時間點承諾返回數據給你。 Promise
有三種狀態:pending/reslove/reject 。pending就是未決,resolve可以理解爲成功,reject可以理解爲拒絕。- 同時Promise常用的三種方法
then
表示異步成功執行後的數據狀態變爲reslovecatch
表示異步失敗後執行的數據狀態變爲rejectall
表示把多個沒有關係的Promise封裝成一個Promise對象使用then返回一個數組數據。 - 下面一起來看看如何使用Promise:
Promise 構造函數有兩個變量function f() { let promise = new Promise((resolve, reject) => { //模擬異步 setTimeout(()=>{ resolve('prom') },1000) }) return promise; }
resolve
用於返回異步執行成功的函數reject
用於返回異步執行失敗的函數。配合then與catch一起使用 - 使用then獲取數據
使用then方法 獲取到上一步resolve返回的數據function f1() { //返回一個Promise用於下一次調用then return f().then(data=>{ // 返回的數據用於下一次then使用 return data+'ise' }) }
- 獲取數據
如果猜的不錯輸出應該爲promisef1().then(data=>{ console.log(data) }) console.log("hello word")
輸出了promise 說明上面是正確的,並且先輸出 hello word 後輸出promise 說明Promise
是異步執行的
-
-
但是如果過多的使用then 也會照成新的執行流程問題。所以我們的另一位主角登場了,那就是ES6中的
Generator
(生成器)Generator
(生成器)是一種有效利用內存的機制,一邊循環一邊計算生成數值的機制。通過配合Promise可以更加優雅的寫異步代碼- 下面我們來試試:
首先我們先用Promise分裝一個異步請求模擬ajax。function f1() { let promise = new Promise((resolve, reject) => { setTimeout(function () { resolve("hello word") }) }) return promise; }
- 構建一個生成器函數
構建生成器非常簡單 只需要在函數方法名前面加一個function *f() { let x = yield f1(); console.log("ni hao") }
*
這個函數就是一個生成器函數,可能有人注意到函數體中有一個yield
關鍵字(學過python應該知道),簡單點說yield
類似return
也是返回值的,區別在於當程序 執行到yield後會返回yield後面的表達式,並且程序暫停在這裏保存當前值狀態,程序只是暫停在這裏並沒有中止。 - 獲取生成器的值
使用next()方法可以獲取到yield第一次暫停的值返回的是var it = f(); it.next().value.then(data=>{ console.log(data) }) console.log(123) it.next() console.log(it.next())
{ value: 值 done: true }
value表示yield返回的值,done表示是否迭代完畢。當然也可以使用netx(10)來給yield設置下次執行的值。 - 輸出結果
通過結果我們看到生成器也是異步執行。
-
當然生成器不是最完美的,它的語法讓人難以理解,所以ES7推出了
async/await
(異步等待),多麼貼切。- 封裝異步請求
上面講過了這裏不再贅述。function f() { return new Promise((resolve, reject) => { setTimeout(()=>{ resolve("hello word") },1000) }) }
- 使用異步函數
函數前面加async function a(){ var data = await f(); return data; }
async
表示該函數是一個異步函數await
表示等待一個異步值的到來, - 獲取值
異步函數返回的是一個Promise對象,到這裏是不是豁然開朗了,return返回的值通過使用then來進行獲取,到這裏有人會問這個不是和生成器一樣嗎只是把* 換成async ,yield緩存await 嗎? 沒錯確實一樣,但是生成器返回的是一個迭代器,而 異步函數返回的是一個Promise,異步函數可以以更加方便的同Promise結合使用來書寫同步代碼風格的異步執行。var a = a() a.then(data=>{ console.log(data) })
- 封裝異步請求