本章介紹三種異步處理方案:ios
若是想要深刻了解 promise
、generator+co
、async/await
的話,建議參考個人這篇文章《深刻理解 promise、generator+co、async/await 用法》git
回調函數應該屬於最簡單粗暴的一種方式,主要表現爲在異步函數中將一個函數進行參數傳入,當異步執行完成以後執行該函數編程
話很少說,上代碼:axios
// 有三個任務console.log(1)console.log(2)console.log(3)
// 過5s執行任務1,任務1執行完後,再過5s執行任務2.....
window.setTimeout(function(){
console.log(1)
window.setTimeout(function(){
console.log(2)
window.setTimeout(function(){
console.log(3)
},5000)
},5000)
},5000)
複製代碼
看出這種方式的缺點了嗎?沒錯,試想,若是再多幾個異步函數,代碼總體的維護性,可讀性都變的極差,若是出了bug,修復的排查過程也變的極爲困難,這個即是所謂的 回調函數地獄。promise
promise簡單的說就是一個容器,裏面保存着某個將來纔會結束的時間(一般是一個異步操做)的結果。從語法上說,promise就是一個對象,從它能夠獲取異步操做的消息。promise提供統一的API,各類異步操做均可以用一樣的方法處理。bash
如何理解:異步
promise有所謂的 4 3 2 1async
4大術語
必定要結合異步操做來理解
既然是異步,這個操做須要有個等待的過程,從操做開始,到獲取結果,有一個過程的異步編程
- 解決(fulfill)指一個 promise 成功時進行的一系列操做,如狀態的改變、回調的執行。雖然規範中用 fulfill 來表示解決,但在後世的 promise 實現多以 resolve 來指代之
- 拒絕(reject)指一個 promise 失敗時進行的一系列操做
- 終值(eventual value)所謂終值,指的是 promise 被解決時傳遞給解決回調的值,因爲 promise 有一次性的特徵,所以當這個值被傳遞時,標誌着 promise 等待態的結束,故稱之終值,有時也直接簡稱爲值(value)
- 據因(reason)也就是拒絕緣由,指在 promise 被拒絕時傳遞給拒絕回調的值
3種狀態
在異步操做中,當操做發出時,須要處於等待狀態
當操做完成時,就有相應的結果,結果有兩種:函數
- 成功了
- 失敗了
一共是3種狀態,以下:
- 等待態(Pending (也叫進行態)
- 執行態(Fulfilled)(也叫成功態)
- 拒絕態(Rejected) (也叫失敗態)
針對每一種狀態,有一些規範:
等待態(Pending)
處於等待態時,promise 需知足如下條件:
- 能夠遷移至執行態或拒絕態
執行態(Fulfilled)
處於執行態時,promise 需知足如下條件:
- 不能遷移至其餘任何狀態
- 必須擁有一個不可變的終值
拒絕態(Rejected)
處於拒絕態時,promise 需知足如下條件:
- 不能遷移至其餘任何狀態
- 必須擁有一個不可變的據因
2種事件
針對3種狀態,只有以下兩種轉換方向:
- pending –> fulfilled
- pendeing –> rejected
在狀態轉換的時候,就會觸發事件:
- 若是是pending –> fulfiied,就會觸發onFulFilled事件
- 若是是pendeing –> rejected,就會觸發onRejected事件
在調用resolve方法或者reject方法的時候,就必定會觸發事件
須要註冊onFulFilled事件 和 onRejected事件
針對事件的註冊,Promise對象提供了then方法,以下:
promise.then(onFulFilled,onRejected)
針對 onFulFilled,會自動提供一個參數,做爲終值(value)
針對 onRejected,會自動提供一個參數,做爲據因(reason)
1個對象
promise
注:只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘的操做都沒法改變這個狀態
簡單來說,就仍是promise中有着三種狀態pending,fulfilled,rejected。在代碼中咱們能夠控制狀態的變動
new Promise(function(resolve,reject){
console.log("pending");
console.log("pending");
resolve();
reject();
})
複製代碼
建立一個Promise對象須要傳入一個函數,函數的參數是resolve和reject,在函數內部調用時,就分別表明狀態由pending=>fulfilled(成功),pending=>rejected(失敗)
一旦promise狀態發生變化以後,以後狀態就不會再變了。好比:調用resolve以後,狀態就變爲fulfilled,以後再調用reject,狀態也不會變化
在建立promise對象,只須要根據需求,轉換狀態便可。無非就是調用兩個函數:
Promise對象在建立以後會馬上執行,所以通常的作法是使用一個函數進行包裝,而後return一個promise對象
function betray(){
return new Promise(function(resolve,reject){
...//異步操做
})
}
複製代碼
在使用時能夠經過promise對象的內置方法then進行調用,then有兩個函數參數,分別表示promise對象中調用resolve和reject時執行的函數
function betray(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve();
},1000)
})
}
betray().then(function(){
...//對應resolve時執行的邏輯
},function(){
...//對應reject時執行的邏輯
})
複製代碼
也能夠用 catch 來執行失敗態
catch方法,用於註冊 onRejected 回調
在這裏要明白兩件事情:
使用以下:
betary().then(res=>...//對應resolve時執行的邏輯).catch(err=>...//對應reject時執行的邏輯)
複製代碼
可使用多個then來實現鏈式調用,then的函數參數中會默認返回promise對象
betray().then(function(){
...//對應resolve時執行的邏輯
},function(){
...//對應reject時執行的邏輯
})
.then(function(){
...//上一個then返回的promise對象對應resolve狀態時執行的邏輯
},function(){
...//上一個then返回的promise對象對應reject狀態時執行的邏輯
})
複製代碼
使用promise來解決回調地獄的作法就是使用then的鏈式調用
function fnA(){
return new Promise(resolve=>{
...//異步操做中resolve
})
}
function fnB(){
return new Promise(resolve=>{
...//異步操做中resolve
})
}
function fnC(){
return new Promise(resolve=>{
...//異步操做中resolve
})
}
fnA()
.then(()=>{
return fnB()
})
.then(()=>{
return fnC()
})
複製代碼
特色是:
async、await是什麼?
async顧名思義是「異步」的意思,async用於聲明一個函數是異步的。而await從字面意思上是「等待」的意思,就是用於等待異步完成。而且await只能在async函數中使用
一般async、await都是跟隨Promise一塊兒使用的。爲何這麼說呢?由於async返回的都是一個Promise對象同時async適用於任何類型的函數上。這樣await獲得的就是一個Promise對象(若是不是Promise對象的話那async返回的是什麼 就是什麼);
await獲得Promise對象以後就等待Promise接下來的resolve或者reject。
async、await解決了什麼?
傳統的回調地獄式寫法:
getData(a=>{
getMoreData(a,b=>{
getMoreData(b,c=>{
console.log(c)
});
});
});
//不行了,再多寫要迷了
複製代碼
Promise改進後的寫法:
getData()
.then(a=>getMoreData(a))
.then(b=>getMoreData(b))
.then(c=>getMoreData(c))
複製代碼
async/await改進後:
(async()=>{
const a = await getData;
const b = await.getMoreData(a);
const c = await.getMoreData(b);
const d = await.getMoreData(c);
})();
複製代碼
async、await寫法
先來看看同步寫法:
console.log(1);
setTimeout(function () {
console.log(2);
}, 1000);
console.log(3);
複製代碼
輸出結果:
1
3
2
複製代碼
能夠看到輸出的順序並非咱們代碼中所寫的那樣,下面來看下async、await是如何解決這個問題的
(async function () {
console.log(1);
await new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(2);
resolve();
}, 1000);
});
console.log(3);
}())
複製代碼
輸出結果:
1
2
3
複製代碼
能夠看到這種寫法的輸出已經符合了咱們的預期
async 的定義:
await 的定義:
常規的promise對象會被js先暫存到eventloop(事件隊列)中,由於js是單線程執行的,等執行棧空了以後,纔會將事件隊列中的事件取出放入執行棧中執行
上述代碼中先是將整段代碼改形成了一個async(async能夠用於任何函數)函數,而後又將setTimeOut改形成了一個Promise對象
下面簡單介紹一下第三方的Promise庫
對開發中使用promise進行小結:
注意:axios和fetch必須使用promise方式,如:
針對本身封裝promise對象,又能夠有以下兩種方式:
好比,針對第三方的promise庫,有兩個知名的庫:
能夠利用bluebird 和 q.js 快速的生成promise對象。
以bluebird爲例,在服務端演示其用法。
第一步:安裝
第二步:使用
是否是以爲 so-easy
以上就是解決JS異步的三種方法,還有好多不足之處,但願能夠繼續學習和深刻理解