什麼是promise?
簡單的說它是一個非同步流程的控制手段。是一個代表了非同步操作最終完成或者失敗的物件。
promise的優點
- promise解決了回撥地獄的問題
- promise可以支援多個併發的請求
- promise的錯誤傳播機制可以統一的處理錯誤資訊
回撥地獄問題
在傳統的ajax呼叫過程中,下面以jquery.ajax為例。如果需求中要多次進行ajax互動,並且上一次的返回結果還要被下一次的ajax使用,程式碼基本上會變成:
$.ajax({
type: "POST", url: "some.php", data: "name=John&
location=Boston", success: function(msg){
$.ajax({
type: "POST", url: "some.php", data: msg, success: function(msg2){
...//多次呼叫
}
});
}
});
複製程式碼
現在還只是兩次呼叫關係,如果是多次呼叫將會引發下面的問題
- 多次呼叫不利於程式碼的管理於維護
- 發生錯誤時不能及時準備的定位錯誤位置
- 只要又一次不成功就不能進行下面的邏輯,不方便進行錯誤處理。
promise的鏈式呼叫就很好的解決了這個問題
Promise的三種狀態
- Pending Promise物件例項建立時候的初始狀態
- resolve 可以理解為成功的狀態
- Reject 可以理解為失敗的狀態
promise 中的狀態只能是從等待狀態轉換到成功狀態或者失敗狀態,且狀態轉變之後不可逆。
一個簡單的promise
let p = new Promise((resolve, reject) =>
{
console.log(1)
});
console.log(2)//1 2複製程式碼
promise裡面只接受一個引數,叫做執行器函式,這個函式會同步執行。也就是說上面程式碼中的箭頭函式被同步執行,得到的結果也就是1和2
promise中的then
每一個promise的例項上都有一個then方法,這個方法上有兩個引數,一個是成功的回撥,一個是失敗的回撥。而取決於成功或者失敗的是promise的執行器函式中執行的是成功還是失敗。
let p = new Promise((resolve, reject) =>
{
console.log(1);
resolve();
//呼叫resolve會走到then的成功回撥 //reject();
//呼叫resolve會走到then的失敗回撥
});
p.then( () =>
{
console.log('成功')
} , () =>
{
console.log('失敗')
}) //1 成功複製程式碼
如果既不呼叫resolve也不呼叫reject,promise則一直處於等待狀態,也就不會走到then方法。
let p = new Promise((resolve, reject) =>
{
console.log(1);
});
p.then( () =>
{
console.log('成功')
} , () =>
{
console.log('失敗')
}) //1 複製程式碼
如果你既呼叫resolve也呼叫reject,那麼誰在前面執行就走誰的對應回撥函式
let p = new Promise((resolve, reject) =>
{
console.log(1);
resolve()//先呼叫成功 reject()
});
p.then( () =>
{
console.log('成功')
} , () =>
{
console.log('失敗')
}) //1 成功複製程式碼
let p = new Promise((resolve, reject) =>
{
console.log(1);
reject()//先呼叫失敗 resolve()
});
p.then( () =>
{
console.log('成功')
} , () =>
{
console.log('失敗')
}) //1 失敗複製程式碼
如果程式碼出錯則會直接走reject的回撥
let p = new Promise((resolve, reject) =>
{
console.log(1);
throw new Error('出錯了~')
});
p.then( () =>
{
console.log('成功')
} , () =>
{
console.log('失敗')
}) //1 失敗複製程式碼
一個promise的例項可以then多次
let p = new Promise((resolve, reject) =>
{
resolve('成功了');
});
p.then((data) =>
{
console.log(data)//成功了
});
p.then((data) =>
{
console.log(data)//成功了
})複製程式碼
利用promise解決回撥地獄
能夠規避非同步操作中回撥地獄的問題,其本質取決於promise的鏈式呼叫。假設需求如下,a.txt檔案的內容為b.txt,b.txt檔案的內容是一段描述文字,現在要求用a.txt的得到最終的描述文字,程式碼如下:
let fs = require('fs');
//首先將非同步方法封裝在一個promise中,非同步結果成功呼叫resolve方法,失敗呼叫reject方法。function read(url) {
return new Promise((resolve, reject) =>
{
fs.readFile(url, 'utf8', function (err, data) {
if (err) reject();
resolve(data);
})
})
}//因為read方法返回的是一個promise,所以可以使用promise的then方法read('a.txt').then((data) =>
{//第一次非同步成功後拿到結果繼續返回一個promise可以實現鏈式呼叫。 console.log(data);
//b.txtreturn read(data);
}, (err) =>
{
}).then((data) =>
{//最後兩次的結果分別對應兩次非同步的返回內容 console.log(data)//描述文字
}, (err) =>
{
})複製程式碼
總結:如果一個promise的then方法中還返回另一個promise,那麼這個promise的成功狀態會走到外層promise的下一次then方法的成功,如果失敗,返回外層promise下一次then的失敗。
promise的鏈式呼叫
- 如果then中返回的是一個普通值,就會走到下一次then的成功回撥。
read().then((data) =>
{return 111
}, (err) =>
{
}).then((data) =>
{console.log(data)//111
}, (err) =>
{
})複製程式碼
- 如果then中返回的是一個錯誤,就會走到下一次then的失敗回撥。
read().then((data) =>
{throw new Error('出錯了~')
}, (err) =>
{
}).then((data) =>
{console.log(data)
}, (err) =>
{console.log(err)//出錯了~
})複製程式碼
- 如果then中什麼也不返回,就會走到下一次then的成功回撥,得到的值為undefined。
read().then((data) =>
{cons.log(111)
}, (err) =>
{
}).then((data) =>
{console.log(data)//undefined
}, (err) =>
{
})複製程式碼
- 如果想統一處理錯誤內容,可以使用catch。
read().then((data) =>
{throw new Error('出錯了~')
}, (err) =>
{
}).then((data) =>
{
}, (err) =>
{
}).catch((err)=>
{
//錯誤處理
})複製程式碼
- 統一處理錯誤後,還可以使用then。
read().then((data) =>
{throw new Error('出錯了~')
}, (err) =>
{
}).then((data) =>
{
}, (err) =>
{
}).catch((err)=>
{
//錯誤處理
}).then((data) =>
{
}, (err) =>
{
})複製程式碼
Promise.all()
all方法可以處理多個請求併發的問題。引數是一個陣列。all方法呼叫後會返回一個新的promise。
let fs = require('fs');
function read(url) {
return new Promise((resolve, reject) =>
{
fs.readFile(url, 'utf8', function (err, data) {
if (err) reject();
resolve(data);
})
})
}Promise.all([read('1.txt'), read('2.txt')]).then((data) =>
{
console.log(data)//[ '文字1內容', '文字2內容' ]
}, (err) =>
{
console.log(err);
})複製程式碼
在all方法中一個失敗了就全部失敗,所以都成功了才會走成功回撥。
Promise.all([read('1.txt'), read('3.txt')]).then((data) =>
{
console.log(data)
}, (err) =>
{
console.log('失敗了');
//失敗了
})複製程式碼
Promise.race()
多個請求中,誰的返回資料最快,結果就是誰
Promise.race([read('1.txt'), read('2.txt')]).then((data) =>
{
console.log(data)////文字2內容
}, (err) =>
{
console.log('失敗了');
})複製程式碼
Promise.resolve()
返回一個成功的Promise
Promise.resolve('123').then((data) =>
{
console.log(data)//123
})複製程式碼
Promise.reject()
返回一個失敗的Promise
Promise.reject('123').then((data) =>
{
console.log('err', data)//err 123
})複製程式碼
如果你想手寫一個promise,請戳下面的連結:手寫promise