查看了
阮一峰ES6
借鑒里面的內(nèi)容總結(jié)
什么是 Promise
Promise 是異步編程的一種解決方案, 沒有
Promise
之前, 使用的是 回調(diào)函數(shù)和事件
回調(diào)地獄
- 以前沒有
Promise
時, 我們使用回調(diào)
來解決異步問題, 但一旦回調(diào)的次數(shù)變多, 回調(diào)里面嵌套回調(diào), 代碼就變得難以維護(hù)
// 當(dāng)一個數(shù)據(jù)返回, 使用結(jié)果來請求另一個數(shù)據(jù), 等待結(jié)果再次請求其它數(shù)據(jù)... , 無窮盡也
<script>
$.ajax({
type: 'get',
url: './user.json',
success: (res) => {
const { id } = res
$.ajax({
type: 'get',
url: './tel.json',
success: res => {
const { tel } = res
$.ajax({
type: 'get',
url: './tel.json',
success: res => {
// TODO: 請求...
}
})
}
})
}
})
</script>
ES6 -> Promise
Promise
解決了上面的回調(diào)地獄
-
Promise
特點(diǎn)- 對象的狀態(tài)不受外界影響
- 一旦狀態(tài)改變, 就不會再變
Promise
基本使用
/**
* 接受一個函數(shù)作為參數(shù)
* 參數(shù)函數(shù) 有兩個參數(shù): resolve, reject
*/
new Promise((resolve, reject) => {})
// 得到結(jié)果
[[Prototype]]: Promise
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined
-
Promise
狀態(tài)- 看到上面
new Promise
返回結(jié)果里有一個[[PromsieState]]: pending
-
Promise
有三種狀態(tài):pending(進(jìn)行中) fulfilled(已成功) rejected(以失敗)
- 看到上面
-
改變
Promise
狀態(tài)上面我們知道 Promise 特點(diǎn)就是: 對象狀態(tài)不受外界影響, 那我們應(yīng)該怎么改變 Promise 的狀態(tài)呢?
// 只有異步操作的結(jié)果碍现,可以決定當(dāng)前是哪一種狀態(tài)幅疼,任何其他操作都無法改變這個狀態(tài) // Promise 狀態(tài)從 pending -> fulfilled const p1 = new Promise((resolve, reject) => resolve()) console.log(p1) // [[PromiseState]]: "fulfilled" // Promise 狀態(tài)從 pending -> rejected const p2 = new Promise((resolve, reject) => reject()) console.log(p2) // [[PromiseState]]: "rejected" // Promise 狀態(tài)一旦改變. 就不會再變 const p3 = new Promise((resolve, reject) => { resolve() reject() }) console.log(p3) // [[PromiseState]]: "fulfilled" const p4 = new Promise((resolve, reject) => { reject() resolve() }) console.log(p4) // [[PromiseState]]: "rejected"
-
得到
Promise
的結(jié)果// 得到成功的結(jié)果 const p1 = new Promise((resolve, reject) => { resolve('成功了') }) console.log(p1) // [[PromiseResult]]: "成功了" p1.then(res => console.log(res)) // 成功了 // 得到失敗的結(jié)果 const p2 = new Promise((resolve, reject) => { reject('失敗了') }) console.log(p2) // [[PromiseResult]]: "失敗了" p2.catch(err => console.log(err)) // 失敗了
使用 Promise 方法
then 方法
- Promise 實例具有
then
方法,也就是說昼接,then
方法是定義在原型對象Promise.prototype
上的-
then
方法的第一個參數(shù)是resolved
狀態(tài)的回調(diào)函數(shù)爽篷,第二個參數(shù)是rejected
狀態(tài)的回調(diào)函數(shù),它們都是可選的 -
then
方法返回的是一個新的Promise
實例, 狀態(tài)是pending
可以采用鏈?zhǔn)綄懛?/li>
-
// then 接收兩個參數(shù), 都是可選的, 參數(shù)都是函數(shù), 返回值也是 Promise 實例
const p1 = new Promise((resolve, reject) => {
// 實參
resolve('成功')
})
// [[PromiseState]]: "fulfilled"
// resolve 實參 傳給 第一個函數(shù)的形參
p1.then((res) => {
console.log(res, '成功時的回調(diào)') // 成功 成功時的回調(diào)
}, (err) => {
console.log(err, '失敗時的回調(diào)')
})
const p2 = new Promise((resolve, reject) => {
reject('失敗')
})
p2.then((res) => {
console.log(res, '成功時的回調(diào)')
}, (err) => {
console.log(err, '失敗時的回調(diào)') // 失敗 失敗時的回調(diào)
})
-
Promise
狀態(tài)不改變 不會執(zhí)行.then()
里面的方法
new Promise((resolve, reject) => {
}).then((res) => {
console.log(res, '成功時的回調(diào)')
}, (err) => {
console.log(err, '失敗時的回調(diào)')
})
// 返回結(jié)果 pending
[[Prototype]]: Promise
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined
// 兩次 then, resolve()
new Promise((resolve, reject) => {
resolve()
}).then((res) => {
console.log(res, '成功時的回調(diào)') // undefined '成功時的回調(diào)'
}, (err) => {
console.log(err, '失敗時的回調(diào)')
}).then((res) => {
console.log(res, '成功2') // undefined '成功2'
}, (err) => {
console.log(err, '失敗2')
})
// 在 then 成功里面 return
new Promise((resolve, reject) => {
resolve()
}).then((res) => {
console.log(res, '成功時的回調(diào)') // undefined '成功時的回調(diào)'
return 'yym'
}, (err) => {
console.log(err, '失敗時的回調(diào)')
}).then((res) => {
console.log(res, '成功2') // yym 成功2
}, (err) => {
console.log(err, '失敗2')
})
// 返回結(jié)果 fulfilled
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: undefined
// 進(jìn)入 失敗 的回調(diào)
new Promise((resolve, reject) => {
resolve()
}).then((res) => {
console.log(res, '成功時的回調(diào)')
throw new Error('出錯了')
}, (err) => {
console.log(err, '失敗時的回調(diào)')
}).then((res) => {
console.log(res, '成功2')
}, (err) => {
console.log(err, '失敗2') // Error: 出錯了, '失敗2'
})
catch 方法
-
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的別名慢睡,用于指定發(fā)生錯誤時的回調(diào)函數(shù)
// catch 中的參數(shù)函數(shù)在什么時候執(zhí)行
// 1. 當(dāng) promise 的狀態(tài)從 pending -> rejected
new Promise((resolve, reject) => {
reject()
}).catch(() => {
console.log('失敗了...') // 失敗了...
})
// 2. 當(dāng) promise 代碼執(zhí)行出現(xiàn)錯誤
new Promise((resolve, reject) => {
throw new Error('錯誤')
}).catch(() => {
console.log('失敗了...')
})
-
[引用阮一峰ES6 promise]
Promise 對象的錯誤具有“冒泡”性質(zhì)逐工,會一直向后傳遞,直到被捕獲為止漂辐。也就是說钻弄,錯誤總是會被下一個catch
語句捕獲
// 一共有三個 Promise 對象:一個由`getJSON()`產(chǎn)生,兩個由`then()`產(chǎn)生者吁。它們之中任何一個拋出的錯誤窘俺,都會被最后一個`catch()`捕獲
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 處理前面三個Promise產(chǎn)生的錯誤
});
-
catch
最佳實踐- 下面代碼第二種寫法可以捕獲前面
then
方法執(zhí)行中的錯誤,也更接近同步的寫法(try/catch
)复凳。因此瘤泪,建議總是使用catch()
方法,而不使用then()
方法的第二個參數(shù)
- 下面代碼第二種寫法可以捕獲前面
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
-
finally()
方法
-
finally()
方法用于指定不管 Promise 對象最后狀態(tài)如何育八,都會執(zhí)行的操作 -
finally
方法的回調(diào)函數(shù)不接受任何參數(shù)对途,這意味著沒有辦法知道,前面的 Promise 狀態(tài)到底是fulfilled
還是rejected
髓棋。這表明实檀,finally
方法里面的操作惶洲,應(yīng)該是與狀態(tài)無關(guān)的,不依賴于 Promise 的執(zhí)行結(jié)果
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
// finally 實現(xiàn)
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
Promise 其它使用
Promise.all
-
Promise.all()
方法用于將多個 Promise 實例膳犹,包裝成一個新的 Promise 實例
// 接收一個數(shù)組為參數(shù)
Promise.all([a, b, c]).then(() => {})
// a b c 都是 promise 實例, 都從 pending -> fulfilled, Promise.all 才會返回成功 fulfilled
// 只要有一個 rejected, Promise.all 就會失敗
const p1 = new Promise((resolve) => resolve(1))
const p2 = new Promise((resolve) => resolve(2))
const p3 = new Promise((resolve) => resolve(3))
Promise.all([p1, p2, p3]).then(res => {
console.log(res, 'res是個數(shù)組')
})
// [1, 2, 3] 'res是個數(shù)組'
const p1 = new Promise((resolve) => resolve(1))
const p2 = new Promise((resolve) => resolve(2))
const p3 = new Promise((resolve, reject) => reject(3))
Promise.all([p1, p2, p3]).then(res => {
console.log(res, 'res是個數(shù)組')
}).catch(reason => {
console.log(reason, 'reason') // 3 'reason'
})
Promise.allSettled()
有時候恬吕,我們希望等到一組異步操作都結(jié)束了,不管每一個操作是成功還是失敗须床,再進(jìn)行下一步操作
-
Promise.allSettled()
方法接受一個數(shù)組作為參數(shù)铐料,數(shù)組的每個成員都是一個 Promise 對象,并返回一個新的 Promise 對象豺旬。只有等到參數(shù)數(shù)組的所有 Promise 對象都發(fā)生狀態(tài)變更(不管是fulfilled
還是rejected
)钠惩,返回的 Promise 對象才會發(fā)生狀態(tài)變更
const p1 = new Promise((resolve) => resolve(1))
const p2 = new Promise((resolve) => resolve(2))
const p3 = new Promise((resolve, reject) => reject(3))
Promise.allSettled([p1, p2, p3]).then(res => {
console.log(res)
})
// 返回
[
{status: 'fulfilled', value: 1}
{status: 'fulfilled', value: 2}
{status: 'rejected', reason: 3}
]
// 使用 Promise.all 實現(xiàn) Promise.allSettled 效果
const p1 = new Promise((resolve, reject) => reject(1))
const p2 = new Promise((resolve) => resolve(2))
const p3 = new Promise((resolve, reject) => reject(3))
x = promiseList => promiseList.map(promise => promise.then(result => ({status: 'ok', value: result}),reason => ({status: 'not ok', reason}) ))
Promise.all(x([p1, p2, p3])).then(res => console.log(res, 'res...'))
// {status: 'not ok', reason: 1}
// {status: 'ok', value: 2}
// {status: 'not ok', reason: 3}
Promise.race()
-
Promise.race()
方法同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例
const p = Promise.race([p1, p2, p3]);
// 只要`p1`族阅、`p2`篓跛、`p3`之中有一個實例率先改變狀態(tài),`p`的狀態(tài)就跟著改變坦刀。那個率先改變的 Promise 實例的返回值举塔,就傳遞給`p`的回調(diào)函數(shù)
const p1 = new Promise((resolve, reject) => reject(1))
const p2 = new Promise((resolve) => resolve(2))
const p3 = new Promise((resolve, reject) => reject(3))
Promise.race([p1, p2, p3]).then(res => {
console.log(res, 'res')
}).catch(reason => {
console.log(reason, 'reason') // 1, p1 先改變狀態(tài), 是 Promise 實例的返回值
})
Promise.any()
方法接受一組 Promise 實例作為參數(shù),包裝成一個新的 Promise 實例返回
- 只要參數(shù)實例有一個變成
fulfilled
狀態(tài)求泰,包裝實例就會變成fulfilled
狀態(tài);如果所有參數(shù)實例都變成rejected
狀態(tài)计盒,包裝實例就會變成rejected
狀態(tài)
// `Promise.any()`不會因為某個 Promise 變成`rejected`狀態(tài)而結(jié)束
const p1 = new Promise((resolve, reject) => reject(1))
const p2 = new Promise((resolve) => resolve(2))
const p3 = new Promise((resolve, reject) => reject(3))
Promise.any([p1, p2, p3]).then(res => {
console.log(res) // 2 成功的
})
---
const p1 = new Promise((resolve, reject) => reject(1))
const p2 = new Promise((resolve, reject) => reject(2))
const p3 = new Promise((resolve, reject) => reject(3))
Promise.any([p1, p2, p3]).then(res => {
console.log(res)
})
// 返回 rejected
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: AggregateError: All promises were rejected
- 就是
Promise.any()
不會因為某個 Promise 變成rejected
狀態(tài)而結(jié)束渴频,必須等到所有參數(shù) Promise 變成rejected
狀態(tài)才會結(jié)束
Promise.resolve()
有時需要將現(xiàn)有對象轉(zhuǎn)為 Promise 對象,
Promise.resolve()
方法就起到這個作用
-
參數(shù)是一個
Promise
實例- 如果參數(shù)是 Promise 實例北启,那么
Promise.resolve
將不做任何修改卜朗、原封不動地返回這個實例
- 如果參數(shù)是 Promise 實例北启,那么
-
參數(shù)是一個
thenable
對象-
thenable
對象指的是具有then
方法的對象 -
Promise.resolve()
方法會將這個對象轉(zhuǎn)為Promise
對象,然后就立即執(zhí)行thenable
對象的then()
方法
let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function (value) { console.log(value); // 42 });
-
-
參數(shù)是一個原始值
-
Promise.resolve()
方法返回一個新的 Promise 對象咕村,狀態(tài)為resolved
const p = Promise.resolve('Hello'); p.then(function (s) { console.log(s) }); // Hello
-
-
不帶有任何參數(shù)
-
Promise.resolve()
方法允許調(diào)用時不帶參數(shù)场钉,直接返回一個resolved
狀態(tài)的 Promise 對象
// 立即`resolve()`的 Promise 對象,是在本輪“事件循環(huán)”(event loop)的結(jié)束時執(zhí)行懈涛,而不是在下一輪“事件循環(huán)”的開始時 setTimeout(function () { console.log('three'); }, 0); Promise.resolve().then(function () { console.log('two'); }); console.log('one'); // one // two // three
-
Promise.reject()
Promise.reject(reason)
方法也會返回一個新的 Promise 實例逛万,該實例的狀態(tài)為rejected
const p = Promise.reject('出錯了');
// 等同于
const p = new Promise((resolve, reject) => reject('出錯了'))
p.then(null, function (s) {
console.log(s)
});
// 出錯了
Promise.try()
Promise.try
為所有操作提供了統(tǒng)一的處理機(jī)制,所以如果想用then
方法管理流程批钠,最好都用Promise.try
包裝一下
try {
database.users.get({id: userId})
.then(...)
.catch(...)
} catch (e) {
// ...
}
// 上面的方法看起來很笨拙, 可以統(tǒng)一用`promise.catch()`捕獲所有同步和異步的錯誤
Promise.try(() => database.users.get({id: userId}))
.then(...)
.catch(...)
// `Promise.try`就是模擬`try`代碼塊宇植,就像`promise.catch`模擬的是`catch`代碼塊