一、callback
回調地獄:回調函數需要依賴上一層的回調執行完,所以形成了層層嵌套的關係最終形成回調地獄,無疑是不利於閱讀與維護的一種形式。
{
let catList = ''
function f1 (name) {
catList += name + ' '
}
f1('My', f1('name', f1('is', f1('oliver'))))
console.log(catList) // oliver is name My
}
二、Promise
使用Promise對象封裝一個或多個異步操作,它提供統一的多種API,調用方便,它有如下特點:
- 對象的狀態不受外界影響,只由異步操作的結果決定。一個Promise對象代表一個異步操作,每個異步操作有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。
- 一旦異步操作有了結果,狀態就不會再變。Promise對象的狀態改變,只有兩種可能:從pending變爲fulfilled和從pending變爲rejected。
1.then
{
let catList = ''
function f3 (name) {
return new Promise((resolve, reject) => {
catList += name + ' '
resolve(catList)
reject(new Error('error!'))
})
}
f3('My').then(
f3(`name`)
).then(
f3('is')
).then(
f3('oliver')
)
console.log(catList) // My name is oliver
catList = ''
/**
* 關於.then的完善
*/
// 完善:成功回調和失敗回調
f3('My').then(() => {
f3(`name`)
}, err => {
console.log(err)
}).then(() => {
f3('is')
}, err => {
console.log(err)
}).then(() => {
f3('oliver')
}, err => {
console.log(err)
})
console.log(catList) // My name is oliver
catList = ''
// 完善:發生錯誤前(在試錯階段:不試錯怎麼能知道需要執行錯誤回調?)仍然生成空白Promise對象
f3('My').then(() => {
return f3(`name`)
}, (err) => {
console.log(err)
}).then(() => {
return f3('is')
}, (err) => {
console.log(err)
}).then(() => {
return f3('oliver')
}, (err) => {
console.log(err)
})
console.log(catList) // My name is oliver
catList = ''
// 完善:使用catch統一處理錯誤
f3('My').then(() => {
return f3(`name`)
}).then(() => {
return f3('is')
}).then(() => {
return f3('oliver')
}).catch(err => {
console.log(err)
})
console.log(catList) // My name is oliver
}
Promise.prototype.then(onFulfilled, onRejected)是Promise對象原型上的方法(實例方法)
- onFulfilled是resolved狀態的回調函數,必選
- onRejected是rejected狀態的回調函數,可選
- 兩個參數都是函數類型
- 調用宿主是Promise對象
2.catch
場景見上個示例代碼中
f3('My').then(() => {
return f3(`name`)
}).then(() => {
return f3('is')
}).then(() => {
return f3('oliver')
}).catch(err => {
console.log(err)
})
Promise.prototype.catch是Promise對象原型上的方法(實例方法)
- 這裏的catch捕獲的是改變Promise狀態爲reject的錯誤,不能用來捕獲throw的錯誤
3.resolve & reject
/**
* 普通方法中使用Promise實現異步回調
* @param flag
*/
function test (flag) {
if (flag) {
return new Promise((resolve, reject) => {
resolve('success!')
reject(new Error('error!'))
})
} else {
// 重點在這裏:這裏也必須要返回一個Promise對象否則調用時不能使用then, 可直接調用Promise靜態方法
return Promise.reject(new Error('error!'))
}
}
test(1).then((value) => {
console.log(value)
}, (err) => {
console.log(err)
})
resolve 和 reject 都是 Promise 對象的靜態方法,需要通過Promise直接調用
4.all
場景:從三個接口請求數據然後數據聚合,此時並不知道哪個接口速度快,也就是請求不分先後,非鏈式調用Promise對象
{
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]).then(value => {
console.log(value)
})
}
(1)只有p1、p2、p3的狀態都變成fulfilled,Promise的狀態纔會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給Promise的回調函數。
(2)只要p1、p2、p3之中有一個被rejected,Promise的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給Promise的回調函數。
5.race
場景:一張網站重要圖片存放於三個CDN中,調用時
{
const p1 = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(1)
}, 1001)
})
}
const p2 = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(2)
}, 1000)
})
}
const p3 = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(3)
}, 999)
})
}
Promise.race([p1(), p2(), p3()]).then(value => {
console.log(value) // 3
})
}
只要p1、p2、p3之中有一個實例率先改變狀態,Promise的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給Promise的回調函數
拓展: