一俏讹、什么是promise,及其作用
Promise
是ES6中的一個(gè)內(nèi)置對(duì)象纵势,實(shí)際是一個(gè)構(gòu)造函數(shù)踱阿,是JS中進(jìn)行異步編程的新的解決方案。
- 特點(diǎn):
① 三種狀態(tài):pending
(進(jìn)行中)钦铁、resolved
(已完成)软舌、rejected
(已失敗)牛曹。只有異步操作的結(jié)果可以決定當(dāng)前是哪一種狀態(tài)佛点,任何其他操作都不能改變這個(gè)狀態(tài)。
② 兩種狀態(tài)的轉(zhuǎn)化:其一躏仇,從pending
(進(jìn)行中)到resolved
(已完成)恋脚。其二腺办,從pending
(進(jìn)行中)到rejected
(已失斞媸帧)糟描。只有這兩種形式的轉(zhuǎn)變。
③Promise
構(gòu)造函數(shù)的原型對(duì)象上书妻,有then
()和catch
()等方法船响,then
()第一個(gè)參數(shù)接收resolved()
傳來(lái)的數(shù)據(jù),catch()
第一個(gè)參數(shù)接收rejected()
傳來(lái)的數(shù)據(jù) - 作用:
① 通常用來(lái)解決異步調(diào)用問(wèn)題
② 解決多層回調(diào)嵌套的方案
③ 提高代碼可讀性躲履、更便于維護(hù)
二 见间、Promise 基本流程
三、Promise的鏈?zhǔn)秸{(diào)用
我們可以用 promise.then()
工猜,promise.catch()
和 promise.finally()
這些方法將進(jìn)一步的操作與一個(gè)變?yōu)橐亚枚顟B(tài)的 promise
關(guān)聯(lián)起來(lái)米诉。這些方法還會(huì)返回一個(gè)新生成的promise
對(duì)象,這個(gè)對(duì)象可以被非強(qiáng)制性的用來(lái)做鏈?zhǔn)秸{(diào)用篷帅,就像這樣:
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA,handleRejectedA)
.then(handleFulfilledB,handleRejectedB)
.then(handleFulfilledC,handleRejectedC);
// 或者史侣,這樣可能會(huì)更好...
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA)
.then(handleFulfilledB)
.then(handleFulfilledC)
.catch(handleRejectedAny);
過(guò)早地處理被拒絕的 promise
會(huì)對(duì)之后promise
的鏈?zhǔn)秸{(diào)用造成影響。不過(guò)有時(shí)候我們因?yàn)樾枰R上處理一個(gè)錯(cuò)誤也只能這樣做魏身。另一方面惊橱,在沒有迫切需要的情況下,可以在最后一個(gè).catch()
語(yǔ)句時(shí)再進(jìn)行錯(cuò)誤處理箭昵,這種做法更加簡(jiǎn)單税朴。
四、Promise 的基本使用
示例家制,如果當(dāng)前時(shí)間是偶數(shù)就代表成功正林,否則代表失敗
// 1. 創(chuàng)建一個(gè)新的Promise對(duì)象
const p = new Promise((resolve, reject) => { // 執(zhí)行器函數(shù),同步執(zhí)行
// 2. 執(zhí)行異步操作任務(wù)
setTimeout(() => {
const time = Date.now() // 如果當(dāng)前時(shí)間是偶數(shù)就代表成功颤殴,否則代表失敗
// 3.1 如果成功了觅廓,調(diào)用resolve(value)
if (time % 2 === 0) {
resolve('成功的數(shù)據(jù),value = ' + time)
} else {
// 3.2 如果失敗了诅病,調(diào)用reject(reason)
reject('失敗的數(shù)據(jù)哪亿,reason = ' + time)
}
}, 1000);
})
p.then(value => {
// 接受得到成功的value數(shù)據(jù),專業(yè)術(shù)語(yǔ):onResolved
console.log('成功的回調(diào)', value)
}, reason => {
// 接受得到失敗的reason數(shù)據(jù)贤笆,專業(yè)術(shù)語(yǔ):onRejected
console.log('失敗的回調(diào)', reason)
})
五蝇棉、Promise幾種常用方法
5.1 Promise.all()
將多個(gè)Promise封裝成一個(gè)新的Promise,成功時(shí)返回的是一個(gè)結(jié)果數(shù)組芥永,失敗時(shí)篡殷,返回的是最先rejected狀態(tài)的值。
使用場(chǎng)景:一次發(fā)送多個(gè)請(qǐng)求并根據(jù)請(qǐng)求順序獲取和使用數(shù)據(jù)
let promise1 = Promise.resolve(1);
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise2');
}, 2000);
})
let promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise3');
}, 1000);
})
let promise4 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('promise4');
}, 1000);
})
let promise5 = Promise.reject('promise5');
Promise.all([promise1, promise2, promise3]).then((values) => {
//三個(gè)promise都是成功的埋涧,所以會(huì)輸出一個(gè)數(shù)組
console.log(values) // [1, "promise2", "promise3"]
})
Promise.all([promise1, promise2, promise3, promise4]).then((values) => {
console.log(values)
}, (reason) => {
//promise4是失敗的板辽,所以只會(huì)返回promise4的失敗結(jié)果
console.log(reason) //promise4
})
Promise.all([promise1, promise2, promise3, promise4, promise5]).then((values) => {
console.log(values)
}, (reason) => {
//promise4個(gè)promise5都是失敗的奇瘦,但是promise5比promise4最先失敗,所以返回promise5的失敗結(jié)果
console.log(reason) //promise5
})
5.2 Promise.race()
返回一個(gè) promise劲弦,一旦迭代器中的某個(gè)promise解決或拒絕耳标,返回的 promise就會(huì)解決或拒絕。
簡(jiǎn)單來(lái)說(shuō)邑跪,就是多個(gè)Promise中次坡,哪個(gè)狀態(tài)先變?yōu)槌晒蛘呤。头祷啬膫€(gè)Promise的值
let promise1 = new Promise((resolve, reject) => {
//setTimeout第三個(gè)以后的參數(shù)是作為第一個(gè)`func()`的參數(shù)傳進(jìn)去,promise1作為resolve的參數(shù)
setTimeout(resolve, 500, 'promise1');
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'promise2');
});
let promise3 = new Promise((resolve, reject) => {
setTimeout(reject, 500, 'promise3');
});
let promise4 = new Promise((resolve, reject) => {
setTimeout(reject, 100, 'promise4');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value); //promise2 100 <promise1 500 所以輸出: promise2
});
Promise.race([promise3, promise4]).then((value) => {
}, (rejected) => {
console.log(rejected) //promise4 100 < promise3 500 所以輸出: promise4
});
Promise.race([promise2, promise3]).then((value) => {
console.log(value) //promise2 100 < promise3 500 所以會(huì)走成功狀態(tài)画畅,輸出: promise2
}, (rejected) => {
console.log(rejected)
});
Promise.race([promise1, promise4]).then((value) => {
console.log(value)
}, (rejected) => {
console.log(rejected) //promise4 100 < promise1 500 所以會(huì)走失敗狀態(tài)砸琅,輸出: promise4
});
5.3 Promise.any()
Promise.any()
接收一個(gè)Promise
可迭代對(duì)象,只要其中的一個(gè) promise
成功轴踱,就返回那個(gè)已經(jīng)成功的 promise
症脂。如果可迭代對(duì)象中沒有一個(gè) promise
成功(即所有的 promises
都失敗/拒絕),就返回一個(gè)失敗的 promise
和AggregateError
類型的實(shí)例淫僻,它是 Error
的一個(gè)子類诱篷,用于把單一的錯(cuò)誤集合在一起。本質(zhì)上嘁傀,這個(gè)方法和Promise.all()
是相反的兴蒸。
這個(gè)方法用于返回第一個(gè)成功的
promise
。只要有一個(gè)promise
成功此方法就會(huì)終止细办,它不會(huì)等待其他的promise
全部完成橙凳。
不像 Promise.all()會(huì)返回一組完成值那樣(resolved values),我們只能得到一個(gè)成功值(假設(shè)至少有一個(gè)promise
完成)笑撞。當(dāng)我們只需要一個(gè)promise
成功岛啸,而不關(guān)心是哪一個(gè)成功時(shí)此方法很有用的。
同時(shí), 也不像 Promise.race()總是返回第一個(gè)結(jié)果值(resolved/reject)那樣茴肥,這個(gè)方法返回的是第一個(gè)* 成功的* 值坚踩。這個(gè)方法將會(huì)忽略掉所有被拒絕的promise
,直到第一個(gè)promise
成功瓤狐。
const pErr = new Promise((resolve, reject) => {
reject("總是失敗");
});
const pSlow = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "最終完成");
});
const pFast = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "很快完成");
});
Promise.any([pErr, pSlow, pFast]).then((value) => {
console.log(value);
// pFast fulfils first
})
// 期望輸出: "很快完成"
5.4 Promise.allSettled()
Promise.allSettled()
方法返回一個(gè)在所有給定的promise都已經(jīng)fulfilled
或rejected
后的promise瞬铸,并帶有一個(gè)對(duì)象數(shù)組,每個(gè)對(duì)象表示對(duì)應(yīng)的promise結(jié)果础锐。
當(dāng)您有多個(gè)彼此不依賴的異步任務(wù)成功完成時(shí)嗓节,或者您總是想知道每個(gè)promise
的結(jié)果時(shí),通常使用它皆警。
相比之下拦宣,Promise.all()
更適合彼此相互依賴或者在其中任何一個(gè)reject
時(shí)立即結(jié)束。
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"
六、什么是Async/Await鸵隧,及其作用
① async/await是ES7新特性
② async/await是寫異步代碼的新方式绸罗,以前的方法有回調(diào)函數(shù)和Promise
③ async/await是基于Promise實(shí)現(xiàn)的,它不能用于普通的回調(diào)函數(shù)
④ async/await與Promise一樣豆瘫,是非阻塞的
⑤ async/await使得異步代碼看起來(lái)像同步代碼珊蟀,這正是它的魔力所在
async function 用來(lái)定義一個(gè)返回 AsyncFunction 對(duì)象的異步函數(shù)。異步函數(shù)是指通過(guò)事件循環(huán)異步執(zhí)行的函數(shù)靡羡,它會(huì)通過(guò)一個(gè)隱式的 Promise 返回其結(jié)果系洛,俊性。如果你在代碼中使用了異步函數(shù)略步,就會(huì)發(fā)現(xiàn)它的語(yǔ)法和結(jié)構(gòu)會(huì)更像是標(biāo)準(zhǔn)的同步函數(shù)。
await 操作符用于等待一個(gè) Promise 對(duì)象定页。它只能在異步函數(shù) async function 中使用趟薄。
6.1 async 函數(shù)
async 函數(shù)的返回值為 Promise 對(duì)象,async 函數(shù)返回的 Promise 的結(jié)果由函數(shù)執(zhí)行的結(jié)果決定
async function fn1() {
return 1
}
const result = fn1()
console.log(result) // Promise { 1 }
既然是 Promise 對(duì)象典徊,那么我們用 then 來(lái)調(diào)用杭煎,并拋出錯(cuò)誤,執(zhí)行 **onRejected() **且 reason 為錯(cuò)誤信息為“我是錯(cuò)誤”
async function fn1() {
// return 1
// return Promise.resolve(1)
// return Promise.reject(2)
throw '我是錯(cuò)誤'
}
fn1().then(
value => { console.log('onResolved()', value) },
reason => { console.log('onRejected()', reason) } // onRejected() 我是錯(cuò)誤
)
6.2 await 表達(dá)式
await 右側(cè)的表達(dá)式一般為 promise 對(duì)象, 但也可以是其它的值
- 如果表達(dá)式是 promise 對(duì)象, await 返回的是 promise 成功的值
- 如果表達(dá)式是其它值, 直接將此值作為 await 的返回值
function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1000)
}, 1000);
})
}
function fn4() { return 6 }
async function fn3() {
// const value = await fn2() // await 右側(cè)表達(dá)式為Promise卒落,得到的結(jié)果就是Promise成功的value
// const value = await '還可以這樣'
const value = await fn4()
console.log('value', value)
}
fn3() // value 6
await 必須寫在 async 函數(shù)中, 但 async 函數(shù)中可以沒有 await羡铲,如果 await 的 Promise 失敗了, 就會(huì)拋出異常, 需要通過(guò) try...catch 捕獲處理。
function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(1000)
reject(1000)
}, 1000);
})
}
async function fn3() {
try {
const value = await fn2()
} catch (error) {
console.log('得到失敗的結(jié)果', error)
}
}
fn3() // 得到失敗的結(jié)果 1000
七儡毕、區(qū)別:
1)簡(jiǎn)潔的代碼
使用async函數(shù)可以讓代碼簡(jiǎn)潔很多也切,不需要像Promise一樣需要些then,不需要寫匿名函數(shù)處理Promise的resolve值腰湾,也不需要定義多余的data變量雷恃,還避免了嵌套代碼。
2) 錯(cuò)誤處理:
Promise 中不能自定義使用 try/catch 進(jìn)行錯(cuò)誤捕獲费坊,但是在 Async/await 中可以像處理同步代碼處理錯(cuò)誤
//#Promise
const makeRequest = () => {
try {
getJSON()
.then(result => {
// this parse may fail
const data = JSON.parse(result)
console.log(data)
})
// uncomment this block to handle asynchronous errors
// .catch((err) => {
// console.log(err)
// })
} catch (err) {
console.log(err)
}
}
//#Async/await
const makeRequest = async () => {
try {
// this parse may fail
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}
3)條件語(yǔ)句
條件語(yǔ)句也和錯(cuò)誤捕獲是一樣的倒槐,在 Async 中也可以像平時(shí)一般使用條件語(yǔ)句
//#Promise
const makeRequest = () => {
return getJSON()
.then(data => {
if (data.needsAnotherRequest) {
return makeAnotherRequest(data)
.then(moreData => {
console.log(moreData)
return moreData
})
} else {
console.log(data)
return data
}
})
}
//#Async
const makeRequest = async () => {
const data = await getJSON()
if (data.needsAnotherRequest) {
const moreData = await makeAnotherRequest(data);
console.log(moreData)
return moreData
} else {
console.log(data)
return data
}
}
4)中間值
在一些場(chǎng)景中,也許需要 promise1 去觸發(fā) promise2 再去觸發(fā) promise3附井,這個(gè)時(shí)候代碼應(yīng)該是這樣的
const makeRequest = () => {
return promise1()
.then(value1 => {
// do something
return promise2(value1)
.then(value2 => {
// do something
return promise3(value1, value2)
})
})
}
如過(guò) promise3 不需要 value1讨越,嵌套將會(huì)變得簡(jiǎn)單。如果你有強(qiáng)迫癥永毅,則將值1&2使用 promise.all() 分裝起來(lái)把跨。
const makeRequest = () => {
return promise1()
.then(value1 => {
// do something
return Promise.all([value1, promise2(value1)])
})
.then(([value1, value2]) => {
// do something
return promise3(value1, value2)
})
}
但是使用 Async 就會(huì)變得很簡(jiǎn)單
const makeRequest = async () => {
const value1 = await promise1()
const value2 = await promise2(value1)
return promise3(value1, value2)
}
錯(cuò)誤棧
如過(guò) Promise 連續(xù)調(diào)用,對(duì)于錯(cuò)誤的處理是很麻煩的卷雕。你無(wú)法知道錯(cuò)誤出在哪里节猿。
const makeRequest = () => {
return callAPromise()
.then(() => callAPromise())
.then(() => callAPromise())
.then(() => callAPromise())
.then(() => callAPromise())
.then(() => {
throw new Error("oops");
})
}
makeRequest()
.catch(err => {
console.log(err);
// output
// Error: oops at callAPromise.then.then.then.then.then (index.js:8:13)
})
但是對(duì)于 Async 就不一樣了
const makeRequest = async () => {
await callAPromise()
await callAPromise()
await callAPromise()
await callAPromise()
await callAPromise()
throw new Error("oops");
}
makeRequest()
.catch(err => {
console.log(err);
// output
// Error: oops at makeRequest (index.js:7:9)
})
6)調(diào)試
async/await能夠使得代碼調(diào)試更簡(jiǎn)單。2個(gè)理由使得調(diào)試Promise變得非常痛苦:
- 《1》不能在返回表達(dá)式的箭頭函數(shù)中設(shè)置斷點(diǎn)
- 《2》如果你在.then代碼塊中設(shè)置斷點(diǎn),使用Step Over快捷鍵滨嘱,調(diào)試器不會(huì)跳到下一個(gè).then峰鄙,因?yàn)樗粫?huì)跳過(guò)異步代碼。
使用await/async時(shí)太雨,你不再需要那么多箭頭函數(shù)吟榴,這樣你就可以像調(diào)試同步代碼一樣跳過(guò)await語(yǔ)句。