Promise 是 JavaScript 異步編程中的重要概念,異步抽象處理對象,是目前比較流行 Javascript 異步編程解決方案之一
回調函數 / 回調地獄
1秒鐘之后輸出 fn1, 再過1秒輸出 fn2, 再過1秒輸出 fn3。
即當我們需要發送多個異步請求,并且每個請求之間需要相互依賴時。這時 我們以嵌套方式來解決。形成了所謂的 "回調地獄"。
function fn1(callback) {
setTimeout(()=>{
console.log('fn1')
callback()
}, 1000)
}
function fn2(callback) {
setTimeout(()=>{
console.log('fn2')
callback()
}, 1000)
}
function fn3() {
setTimeout(()=>{
console.log('fn3')
}, 1000)
}
fn1(function(){
fn2(function(){
fn3()
})
})
這種編碼方式有以下幾個問題
- 代碼邏輯書寫順序與執行順序不一致,不利于閱讀與維護。
- 異步操作的順序變更時,需要大規模的代碼重構。
- 回調函數基本都是匿名函數,bug 追蹤困難。
Promise 的使用
我們使用 Promise 可以以直觀的方式,來解決 "回調地獄"。
即 Promise 處理多個相互關聯的異步請求。
const promise = new Promise((resolve, reject) => {
// 異步處理
// 處理結束后、調用 resolve 或 reject
// 如果成功就調用 resolve
// 如果失敗就調用 reject
})
promise.then(successFn, errorFn)
Promise 是一個對象,對象里存儲一個狀態,這個狀態是可以隨著內部的執行轉化的,為以下三種狀態之一:
- 等待態(Pending)
- 完成態(Fulfilled)
- 拒絕態(Rejected)。
一開始,我們先設置好等狀態從 pending 變成 fulfilled 和 rejected 的預案(當成功后我們做什么,失敗時我們做什么)。
Promise 啟動之后,當滿足 成功 的條件時我們讓狀態從 pending 變成 fullfilled (執行 resolve);當滿足 失敗 的條件,我們讓狀態從 pending 變成 rejected(執行 reject)
使用 Promise 解決剛才的回調函數
function fn1() {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log('fn1...')
resolve()
}, 1000)
})
}
function fn2() {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log('fn2...')
resolve()
}, 1000)
})
}
function fn3() {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log('fn3...')
resolve()
}, 1000)
})
}
function onerror() {
console.log('error')
}
fn1().then(fn2).then(fn3).catch(onerror)
Promise 范例
Promise.prototype.then / Promise.prototype.catch
function getIp() {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getIp', true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) // {"ip":"58.100.211.137"}
resolve(retJson.ip)
}
xhr.onerror = function(){
reject('獲取IP失敗')
}
xhr.send()
})
return promise
}
function getCityFromIp(ip) {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) // {"city": "hangzhou","ip": "23.45.12.34"}
resolve(retJson.city)
}
xhr.onerror = function(){
reject('獲取city失敗')
}
xhr.send()
})
return promise
}
function getWeatherFromCity(city) {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getWeatherFromCity?city='+city, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) //{"weather": "晴天","city": "beijing"}
resolve(retJson)
}
xhr.onerror = function(){
reject('獲取天氣失敗')
}
xhr.send()
})
return promise
}
getIp().then(function(ip){
return getCityFromIp(ip)
}).then(function(city){
return getWeatherFromCity(city)
}).then(function(data){
console.log(data)
}).catch(function(e){
console.log('出現了錯誤', e)
})
Promise.all
function getCityFromIp(ip) {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) // {"city": "hangzhou","ip": "23.45.12.34"}
resolve(retJson)
}
xhr.onerror = function(){
reject('獲取city失敗')
}
xhr.send()
})
return promise
}
var p1 = getCityFromIp('10.10.10.1')
var p2 = getCityFromIp('10.10.10.2')
var p3 = getCityFromIp('10.10.10.3')
//Promise.all, 當所有的 Promise 對象都完成后再執行
Promise.all([p1, p2, p3]).then(data=>{
console.log(data)
})
Promise.race
Promise.race 只要有一個 Promise對象 進入 FulFilled 或者 Rejected 狀態的話,就會繼續進行后面的處理。
function getCityFromIp(ip) {
var promise = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://easy-mock.com/mock/5ac2f80c3d211137b3f2843a/promise/getCityFromIp?ip='+ip, true)
xhr.onload = function(){
var retJson = JSON.parse(xhr.responseText) // {"city": "hangzhou","ip": "23.45.12.34"}
resolve(retJson)
}
xhr.onerror = function(){
reject('獲取city失敗')
}
setTimeout(()=>{
xhr.send()
}, Math.random()*1000)
})
return promise
}
var p1 = getCityFromIp('10.10.10.1')
var p2 = getCityFromIp('10.10.10.2')
var p3 = getCityFromIp('10.10.10.3')
//Promise.all, 當所有的 Promise 對象都完成后再執行
Promise.race([p1, p2, p3]).then(data=>{
console.log(data)
})