JavaScript中Promise的使用方法實(shí)例
前言
我還記得我剛開(kāi)始學(xué)習(xí)JavaScript的Promise很多概念都不懂很"懵逼", 現(xiàn)在已經(jīng)工作有小半年時(shí)間了, 整理整理筆記和個(gè)人在工作中的使用情況, 寫(xiě)個(gè)文章記錄一下(PS只記錄怎么使用, 原理太深?yuàn)W功力不夠), 如果有不對(duì)的地方前輩勿噴??
Promise簡(jiǎn)介
Promise也稱(chēng)期約, 是ES6推出的一個(gè)異步解決方案, 可以有效的解決異步函數(shù)嵌套太深("回調(diào)地獄")的問(wèn)題
什么是回調(diào)地獄?
假設(shè)有個(gè)需求需要獲取用戶(hù)的指定數(shù)據(jù)用戶(hù)數(shù)據(jù)3這個(gè)數(shù)據(jù)依賴(lài)用戶(hù)數(shù)據(jù)2而用戶(hù)數(shù)據(jù)2又依賴(lài)于用戶(hù)數(shù)據(jù)1, 所以正確的數(shù)據(jù)獲取數(shù)據(jù)順序?yàn)? 用戶(hù)數(shù)據(jù)1-->用戶(hù)數(shù)據(jù)2-->用戶(hù)數(shù)據(jù)3, 模擬一下使用回調(diào)函數(shù)的寫(xiě)法如下:
Node接口:
const router = require('express').Router();
const data1 = { data: "用戶(hù)數(shù)據(jù)1" };
router.get('/testData1', (req, res) => {
res.json(data1);
})
const data2 = { data: "用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2" };
router.get('/testData2', (req, res) => {
if (req.query.data === data1.data) {
res.json(data2);
} else {
res.status(401).json("參數(shù)錯(cuò)誤");
}
})
router.get('/testData3', (req, res) => {
if (req.query.data === data2.data) {
res.json({ data: "用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3" });
} else {
res.status(401).json("參數(shù)錯(cuò)誤");
}
})
module.exports = router;前端請(qǐng)求代碼:
// 簡(jiǎn)單封裝的 XMLHttpRequest 請(qǐng)求函數(shù)
const baseUrl = "http://localhost:8888/test";
const request = (url, cb) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', baseUrl + url);
xhr.send();
xhr.onreadystatechange = () => {
const { readyState, status, response } = xhr;
if (readyState === 4) {
if (status >= 200 && status <= 299) {
cb(JSON.parse(response));
} else {
throw new Error(response);
}
}
}
}
// 因?yàn)橄乱粋€(gè)請(qǐng)求需要上一個(gè)請(qǐng)求的數(shù)據(jù)所以需要一個(gè)回調(diào)函數(shù)嵌套一個(gè)回調(diào)函數(shù)
request("/testData1", res1 => {
console.log(res1); // => {data: '用戶(hù)數(shù)據(jù)1'}
request(`/testData2?data=${res1.data}`, res2 => {
console.log(res2); // => {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}
request(`/testData3?data=${res2.data}`, res3 => {
console.log("需求需要的數(shù)據(jù)", res3); // => 需求需要的數(shù)據(jù) {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
// ....
})
})
})這個(gè)代碼看著就頭大, 如果需求復(fù)雜的話, 我只能用一張圖表示(這張圖片我也不記得從哪里保存的忘了??)

這種一個(gè)回調(diào)嵌套一個(gè)回調(diào)的代碼可讀性和可維護(hù)性都很差, 被稱(chēng)為"回調(diào)地獄", 而Promise的出現(xiàn)就可以很好的解決這個(gè)問(wèn)題
Promise的特點(diǎn)
Promise對(duì)象有一個(gè)狀態(tài), 這個(gè)狀態(tài)不受外界影響, 狀態(tài)一共分為3種:
- Pending狀態(tài) (進(jìn)行中(又稱(chēng)待定)) 初始狀態(tài)
- Fulfilled狀態(tài) (成功(又稱(chēng)兌現(xiàn)))
- Rejected狀態(tài)(失敗(又稱(chēng)拒絕))
關(guān)于Promise的狀態(tài)叫法下文統(tǒng)一使用成功,失敗
一旦Promise對(duì)象的狀態(tài)改變(成功或失敗)就不會(huì)再變, 是單向的:
Pending(進(jìn)行中) -> Fulfilled(成功)
Pending(進(jìn)行中) -> Rejected(失敗)
創(chuàng)建Promise實(shí)例
創(chuàng)建Promise實(shí)例需要newPromise構(gòu)造函數(shù), 該構(gòu)造函數(shù)接受一個(gè)函數(shù)(處理器函數(shù))作為參數(shù), 該函數(shù)會(huì)收到兩個(gè)參數(shù), 這兩個(gè)參數(shù)分別是resolve和reject(叫什么都行一般還是要語(yǔ)義化名稱(chēng))它們是兩個(gè)函數(shù), 不用自己實(shí)現(xiàn)就可以使用, 如下:
const p = new Promise((resolve, reject) => {
})
console.log(p);
可以看到這個(gè)狀態(tài)默認(rèn)是Pending(進(jìn)行中)
resolve和reject這兩個(gè)參數(shù)(函數(shù))可以在處理器函數(shù)里面調(diào)用(可以傳遞參數(shù)), 這樣將會(huì)改變 Promise 對(duì)象的狀態(tài):
const p = new Promise((resolve, reject) => {
resolve();
})
console.log(p);
當(dāng)調(diào)用resolve函數(shù)時(shí)會(huì)將Priomise對(duì)象的狀態(tài)修改為Fulfilled(成功), 調(diào)用reject函數(shù)則會(huì)將狀態(tài)修改為Rejected(失敗)
then方法
Promise實(shí)例的then方法, 可以接受兩個(gè)參數(shù)(都是函數(shù))分別指定Primise實(shí)例里面狀態(tài)(成功或失敗)改變時(shí)調(diào)用的回調(diào)函數(shù)(并且Promise的then方法是異步的微任務(wù)):
const p = new Promise((resolve, reject) => {
// 將p的狀態(tài)修改為成功
resolve();
})
console.log("同步代碼");
p.then(
() => {
console.log("成功的回調(diào)");
},
() => {
console.log("失敗的回調(diào)");
}
)結(jié)果如下:

反之調(diào)用reject函數(shù)就會(huì)觸發(fā)then方法的第二個(gè)回調(diào)函數(shù), 如果將resolve函數(shù)和reject函數(shù)都調(diào)用只會(huì)生效最先調(diào)用的(因?yàn)闋顟B(tài)時(shí)單向的嘛)
resolve 和 reject 的參數(shù)傳遞
經(jīng)過(guò)上面的測(cè)試我們知道resolve和reject這兩個(gè)函數(shù)是可以修改狀態(tài)并且觸發(fā)Promise的then方法的回調(diào)函數(shù)的, 那么是函數(shù)就可以傳遞參數(shù), 這個(gè)參數(shù)會(huì)被傳遞給對(duì)應(yīng)的then方法的回調(diào)函數(shù)接收到:
const p = new Promise((resolve, reject) => {
// 將p的狀態(tài)修改為成功, 并且傳遞一個(gè)參數(shù)
resolve("ok");
})
console.log("同步代碼");
p.then(
// 這里接收 resolve 函數(shù)傳遞的參數(shù)
res => {
console.log("成功的回調(diào)", res);
},
// 這里接收 reject 函數(shù)傳遞的參數(shù)
err => {
console.log("失敗的回調(diào)");
}
)結(jié)果如下:

then()鏈?zhǔn)秸{(diào)用
了解完then方法以后我們就可以稍微修改一下一開(kāi)始最上面的需求:
// 請(qǐng)求函數(shù)使用 Promise 封裝
const baseUrl = "http://localhost:8888/test";
const request = (url) => {
// 返回一個(gè)Promise實(shí)例
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', baseUrl + url);
xhr.send();
xhr.onreadystatechange = () => {
const { readyState, status, response } = xhr;
if (readyState === 4) {
if (status >= 200 && status <= 299) {
const res = JSON.parse(response);
// 修改狀態(tài)為成功并且把響應(yīng)傳遞過(guò)去
resolve(res);
} else {
// 修改狀態(tài)為失敗也把響應(yīng)傳遞過(guò)去
reject(response);
}
}
}
})
}
// 使用then方法
request("/testData1").then(
res1 => {
console.log(res1); // {data: '用戶(hù)數(shù)據(jù)1'}
request(`/testData2?data=${res1.data}`).then(
res2 => {
console.log(res2); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}
request(`/testData3?data=${res2.data}`).then(
res3 => {
console.log("需求需要的數(shù)據(jù)", res3); // => 需求需要的數(shù)據(jù) {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
}
)
}
)
}
)寫(xiě)完以后發(fā)現(xiàn)好像還不如使用回調(diào)函數(shù)的方式寫(xiě), 看到這里好像Promise還是不能很好的解決回調(diào)嵌套的問(wèn)題; 換個(gè)思路如果我們?cè)趖hen方法中再返回一個(gè)Promise實(shí)例, 那么不就又可以調(diào)用then方法了嗎? 代碼中測(cè)試一下:
const p1 = new Promise((resolve, reject) => {
resolve("p1數(shù)據(jù)");
})
// 這里的p2就是p1.then方法成功回調(diào)里面返回的p2
const p2 = p1.then(
// 這里的res1參數(shù)就是p1的處理器函數(shù)中調(diào)用 resolve("p1數(shù)據(jù)") 傳遞的參數(shù)
res1 => {
console.log(res1); // p1數(shù)據(jù)
// 這里新建一個(gè)新的Promise實(shí)例, 記作p2
const p2 = new Promise((resolve, reject) => {
// 0.5s后修改狀態(tài)
setTimeout(() => {
resolve(res1 + ",p2數(shù)據(jù)");
}, 500);
})
// 將p2返回
return p2;
}
)
const p3 = p2.then(
res2 => {
console.log(res2); // p1數(shù)據(jù),p2數(shù)據(jù)
// 這里和上面的同理
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(res2 + ",p3數(shù)據(jù)");
}, 500);
})
return p3;
}
)
p3.then(
res3 => {
console.log(res3); // p1數(shù)據(jù),p2數(shù)據(jù),p3數(shù)據(jù)
}
)發(fā)現(xiàn)可行后, 把需求代碼再修改一下:
// 這里的p1就是 request("/testData1") 返回的Promise實(shí)例
const p1 = request("/testData1");
// 這里的p2就是p1.then方法成功回調(diào)返回的p2
const p2 = p1.then(
res1 => {
console.log(res1); // {data: '用戶(hù)數(shù)據(jù)1'}
// 再調(diào)用request方法將其返回的Promise實(shí)例記作p2
const p2 = request(`/testData2?data=${res1.data}`);
// 將p2返回
return p2;
}
)
const p3 = p2.then(
res2 => {
console.log(res2); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}
// 這里和上面的同理
const p3 = request(`/testData3?data=${res2.data}`);
return p3;
}
)
p3.then(
res3 => {
console.log("需求需要的數(shù)據(jù)", res3); // 需求需要的數(shù)據(jù) {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
}
)需求實(shí)現(xiàn)是實(shí)現(xiàn)了, 就是代碼有點(diǎn)冗余, 可以精簡(jiǎn)一下, 如下:
request("/testData1").then(
res1 => {
console.log(res1); // {data: '用戶(hù)數(shù)據(jù)1'}
// 這里直接返回 request方法的返回值Promise實(shí)例
return request(`/testData2?data=${res1.data}`);
}
).then(
// 這個(gè)成功回調(diào)時(shí)上一個(gè)then方法返回的Promise實(shí)例的成功回調(diào)
res2 => {
console.log(res2); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}
// 同理
return request(`/testData3?data=${res2.data}`);
}
).then(
// 同理
res3 => {
console.log("需求需要的數(shù)據(jù)", res3); // 需求需要的數(shù)據(jù) {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
}
)上面的代碼格式就像鏈條一樣所以又被稱(chēng)為"鏈?zhǔn)秸{(diào)用"
then()的返回值
經(jīng)過(guò)上面的代碼測(cè)試, then方法除了返回Promise對(duì)象外還可以返回其他任意的值, 返回會(huì)被轉(zhuǎn)換為Promise對(duì)象, 內(nèi)部的狀態(tài)視返回值而定:
const p1 = new Promise(resolve => resolve());
p1.then(() => {
// 這里相當(dāng)于是 return undefined
}).then(
res1 => {
console.log(res1); // undefined
return "hello";
}
).then(
res2 => {
console.log(res2); // hello
return { name: "張三" };
}
).then(
res3 => {
console.log(res3); // { name: "張三" }
// 返回錯(cuò)誤對(duì)象該P(yáng)romise對(duì)象的狀態(tài)也是成功
return new Error("error object");
}
).then(
res4 => {
console.log(res4 instanceof Error); // true
console.log(res4.message); // error object
// 拋出一個(gè)錯(cuò)誤則該P(yáng)romise對(duì)象的狀態(tài)是失敗
throw "thorw error";
}
).then(
() => { },
err => {
console.log(err); // thorw error
}
)catch方法
上面使用then方法的鏈?zhǔn)秸{(diào)用可以解決回調(diào)嵌套太深的問(wèn)題, 但是還沒(méi)處理請(qǐng)求之間的失敗回調(diào)處理, then方法的第二個(gè)回調(diào)就是指定失敗的回調(diào), 但是一般都不使用這個(gè)回調(diào)來(lái)處理錯(cuò)誤, 而是使用catch方法來(lái)失敗, 使用格式如下:
p1.then(
// ...
).then(
// ...
).catch(err => { // 這個(gè)catch可以捕獲這一條調(diào)用鏈上的錯(cuò)誤
// 處理錯(cuò)誤
})finally方法
除了catch方法那自然就有finally方法, finally方法和try...catch...finally中的finally是一樣的作用, 使用格式如下:
p.then(
// ...
).then(
// ...
).catch(err => { // 這個(gè)catch方法可以捕獲這一條調(diào)用鏈上的錯(cuò)誤
// 處理錯(cuò)誤
}).finally(() => { // 無(wú)論成功還是失敗這個(gè)finally中指定的回調(diào)都會(huì)執(zhí)行
// 清除loading, 重置狀態(tài)...
})Promise的方法
Promise.resolve()
立即返回一個(gè)狀態(tài)是成功(Fulfilled) 的 Promise 對(duì)象, 可以傳遞參數(shù), 參數(shù)會(huì)被其返回的Promise實(shí)例的then方法的回調(diào)(異步微任務(wù))接受到:
const p = Promise.resolve("Promise.resolve");
p.then(res => console.log(res)); // Promise.resolve也可以利用Promise.resolve()來(lái)創(chuàng)建一個(gè)微任務(wù):
console.log("同步代碼");
setTimeout(() => console.log("setTimeout"), 0);
const p = Promise.resolve("Promise.resolve");
p.then(res => console.log(res));結(jié)果如下:

Promise.reject()
和Promise.resolve()一樣不過(guò)返回的狀態(tài)是失敗(Rejected) 的Promise對(duì)象(同樣是微任務(wù))
console.log("同步代碼");
setTimeout(() => console.log("setTimeout"), 0);
const p = Promise.reject("Promise.reject");
p.then().catch(err => console.log("Promise.reject"));Promise.all()
Promise.all接收一個(gè)Promise的iterable類(lèi)型(就是可迭代對(duì)象里面存放著Promise實(shí)例, Array, Map, Set都屬于ES6的iterable類(lèi)型), Promise.all 會(huì)等待所有的Promise對(duì)象都完成(或第一個(gè)失敗) , 根據(jù)給定的參數(shù)返回不同的參數(shù)
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p1 data"), 500);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p2 data"), 1000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p3 data"), 1500);
})
// 依次打印數(shù)據(jù)
p1.then(res => console.log(res)); // 0.5s 后打印 p1 data
p2.then(res => console.log(res)); // 1.0s 后打印 p2 data
p3.then(res => console.log(res)); // 1.5s 后打印 p3 data
const proArray = [p1, p2, p3];
Promise.all(proArray).then(resList => {
console.log(resList); // 1.5s后打印數(shù)據(jù) ['p1 data', 'p2 data', 'p3 data']
}).catch(err => {
// 如果在`Promise.all`方法中出現(xiàn)了失敗的狀態(tài), 那么這個(gè)參數(shù)會(huì)是這個(gè)失敗狀態(tài)返回的參數(shù)(如果有的話)
console.error("error: ", err);
})利用Promise.all()方法的特定可以用于同時(shí)發(fā)送多個(gè)請(qǐng)求, 如下例子:
Node接口:
const getRandom = () => Math.random() * 9 + 1;
router.get('/testData4', (req, res) => {
let n = req.query.n;
const random = getRandom();
// 定時(shí)器模擬接口響應(yīng)時(shí)間差
setTimeout(() => {
res.json(`第${++n}個(gè)請(qǐng)求${random}`);
}, random * 50);
});前端發(fā)送多個(gè)請(qǐng)求:
const proArray = [];
for (let i = 0; i < 10; i++) {
// 將10個(gè)請(qǐng)求方法返回的Promsie對(duì)象添加到proArray數(shù)組中
proArray.push(request(`/testData4?n=${i}`));
}
Promise.all(proArray).then(resList => {
for (const res of resList) {
// 每個(gè)請(qǐng)求的返回結(jié)果
console.log(res);
}
})Promise.allSettled()
Promise.allSettled()方法和Promise.all()很類(lèi)似只不過(guò)是接受的期約對(duì)象無(wú)論是成功還是失敗都會(huì)觸發(fā)then方法的成功回調(diào), 每個(gè)期約都會(huì)返回一個(gè)對(duì)象status屬性表示狀態(tài), value表示成功回調(diào)的值, 如果狀態(tài)是失敗的那么失敗回調(diào)的值存儲(chǔ)在reason屬性中:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p1 data"), 500);;
})
const p2 = new Promise((resolve, reject) => {
// p2的Promise實(shí)例的狀態(tài)修改為失敗
setTimeout(() => reject("p2 err"), 1000);
})
const proArray = [p1, p2];
Promise.allSettled(proArray).then(resList => {
console.log(resList); // (2) [{…}, {…}]
for (const res of resList) {
const { status, value, reason } = res;
if (reason) {
console.log(`失敗: ${status}, 原因是: ${reason}`); // 失敗: rejected, 原因是: p2 err
} else {
console.log(`成功: ${status}, 數(shù)據(jù)是: ${value}`); // 成功: fulfilled, 數(shù)據(jù)是: p1 data
}
}
}).catch(err => {
// 這里不會(huì)捕獲p2的失敗的回調(diào)
console.error("error: ", err);
})Promise.race()
Promise.race()和Promise.all()類(lèi)似, 都接收一個(gè)可以迭代的參數(shù), 但是不同之處是
Promise.race()的狀態(tài)變化不是受全部參數(shù)的狀態(tài)影響, 一旦迭代器中的某個(gè)Promise解決或拒絕,返回的 Promise就會(huì)解決或拒絕
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p1 data"), 500);;
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => reject("p2 err"), 1000);
})
const proArray = [p1, p2];
Promise.race(proArray).then(res => {
console.log(res); // p1 data
}).catch(err => {
console.error(err);
})async 和 await
async函數(shù)函數(shù)
async函數(shù)函數(shù)就是使用async關(guān)鍵字聲明的函數(shù)(也叫異步函數(shù)), async函數(shù)和普通的函數(shù)使用沒(méi)有什么區(qū)別:
// 函數(shù)聲明
async function asyncFn1() {
console.log("asyncFn1");
}
// 函數(shù)表達(dá)式
const asyncFn2 = async () => {
console.log("asyncFn2");
}
asyncFn1();
asyncFn2();
// 立即調(diào)用
(async () => {
console.log("asyncFn3");
})();await
await操作符用于等待一個(gè)Promise對(duì)象, 它只能在async function中使用, 使用async+await可以將異步的代碼"變"的跟同步的一樣:
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("data");
}, 1000);
});
// async 函數(shù)
async function asyncFn() {
console.log("asyncFn函數(shù)開(kāi)始執(zhí)行");
// await 會(huì)等待右邊的Promise對(duì)象的狀態(tài)變成功后返回其值
const res = await p;
console.log(res); // data
console.log("asyncFn函數(shù)執(zhí)行完了");
}
// 調(diào)用
asyncFn();上面的代碼會(huì)先輸出"asyncFn函數(shù)開(kāi)始執(zhí)行"后, 等待1s左右輸出"data", 然后再輸出"asyncFn函數(shù)執(zhí)行完了"
注意: 異步函數(shù)不會(huì)阻塞主線程的執(zhí)行, 它是異步的:
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("data");
}, 1000);
});
// async 函數(shù)
async function asyncFn() {
console.log("asyncFn函數(shù)開(kāi)始執(zhí)行");
const res = await p;
console.log(res);
console.log("asyncFn函數(shù)執(zhí)行完了");
}
console.log("hello");
asyncFn();
console.log("javascript");等待大約1s后上面的代碼執(zhí)行結(jié)果如下:

根據(jù)上面的代碼執(zhí)行結(jié)果, 我們發(fā)現(xiàn)異步函數(shù)內(nèi)await關(guān)鍵字會(huì)等待其右邊的Promise對(duì)象的返回值, 等待結(jié)束后, 后面的代碼才會(huì)被執(zhí)行, 利用這個(gè)特性我們可以在JavaScript中可以實(shí)現(xiàn)類(lèi)似Java的Thread.sleep()方法, 如下:
const sleep = async time => new Promise(resolve => setTimeout(resolve, time));
(async () => {
console.log("1");
await sleep(1000);
console.log("2");
await sleep(500);
console.log("2.5");
})();了解完async和await的使用后我們可以使用異步函數(shù)再來(lái)完成我們一開(kāi)始的需求:
// 請(qǐng)求函數(shù)使用 Promise 封裝
const baseUrl = "http://localhost:8888/test";
const request = (url) => {
// 返回一個(gè)Promise實(shí)例
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', baseUrl + url);
xhr.send();
xhr.onreadystatechange = () => {
const { readyState, status, response } = xhr;
if (readyState === 4) {
if (status >= 200 && status <= 299) {
const res = JSON.parse(response);
// 修改狀態(tài)為成功并且把響應(yīng)傳遞過(guò)去
resolve(res);
} else {
// 修改狀態(tài)為失敗也把響應(yīng)傳遞過(guò)去
reject(response);
}
}
}
})
}
async function asyncFn() {
const res1 = await request("/testData1");
console.log(res1); // {data: '用戶(hù)數(shù)據(jù)1'}
const res2 = await request(`/testData2?data=${res1.data}`);
console.log(res2); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}
const res3 = await request(`/testData3?data=${res2.data}`);
console.log(res3); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
}
asyncFn();可以看到使用async和await來(lái)發(fā)送網(wǎng)絡(luò)請(qǐng)求寫(xiě)的代碼很簡(jiǎn)潔, 也很直觀
異步函數(shù)的錯(cuò)誤處理
異步函數(shù)的異常處理可以使用try...catch和catch處理:
try...catch
async function asyncFn() {
let res1, res2, res3;
try {
res1 = await request("/testData1");
try {
if (res1?.data) {
res2 = await request(`/testData2?data=${res1.data}`);
}
try {
if (res2?.data) {
res3 = await request(`/testData3?data=${res2.data}`);
}
} catch (error) {
// 處理res3 error
}
} catch (error) {
// 處理res2 error
}
} catch (error) {
// 處理res3 error
}
}catch
async function asyncFn() {
const res1 = await request("/testData1")
.catch(err => {
// 處理res1 error
});
if (res1?.data) {
const res2 = await request(`/testData2?data=${res1.data}`)
.catch(err => {
// 處理res2 error
});
if (res2?.data) {
const res3 = await request(`/testData3?data=${res2.data}`)
.catch(err => {
// 處理res3 error
});
}
}
}異步函數(shù)同樣適用于Promise的一些靜態(tài)方法
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p1 data"), 500);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p2 data"), 1000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p3 data"), 1500);
})
const proArray = [p1, p2, p3];
async function asyncFn() {
const list = await Promise.all(proArray);
console.log(list); // ['p1 data', 'p2 data', 'p3 data']
}
asyncFn();for await...of
一個(gè)數(shù)組中存儲(chǔ)多個(gè)Promise對(duì)象我們可以通過(guò)Promise.all獲取或者通過(guò)循環(huán)來(lái)等待其返回值, 我們使用循環(huán):
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p1 data"), 500);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p2 data"), 1000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p3 data"), 1500);
})
const proArray = [p1, p2, p3];
async function asyncFn() {
for (const pro of proArray) {
// 這里不要忘記 await
const res = await pro;
console.log(res);
}
}
asyncFn();ES9開(kāi)始有一個(gè)新語(yǔ)法就是for await..of可以自動(dòng)等待每次循環(huán)的項(xiàng)
async function asyncFn() {
for await (const item of proArray) {
console.log(item);
}
}
asyncFn();參考:
- MDN Promise
- MDN async
- MDN for await...of
- JavaScript權(quán)威指南
- 阮一峰ES6入門(mén)之promise
總結(jié)
到此這篇關(guān)于JavaScript中Promise使用的文章就介紹到這了,更多相關(guān)JS Promise的使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS獲取url參數(shù),JS發(fā)送json格式的POST請(qǐng)求方法
下面小編就為大家分享一篇JS獲取url參數(shù),JS發(fā)送json格式的POST請(qǐng)求方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
js實(shí)現(xiàn)股票實(shí)時(shí)刷新數(shù)據(jù)案例
下面小編就為大家?guī)?lái)一篇js實(shí)現(xiàn)股票實(shí)時(shí)刷新數(shù)據(jù)案例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
利用百度echarts實(shí)現(xiàn)圖表功能簡(jiǎn)單入門(mén)示例【附源碼下載】
這篇文章主要介紹了利用百度echarts實(shí)現(xiàn)圖表功能簡(jiǎn)單,結(jié)合簡(jiǎn)單示例形式分析了echarts插件的圖標(biāo)繪制功能相關(guān)實(shí)現(xiàn)技巧,并附帶源碼供讀者下載參考,需要的朋友可以參考下2019-06-06
javascript框架設(shè)計(jì)之類(lèi)工廠
這篇文章主要介紹了javascript框架設(shè)計(jì)之類(lèi)工廠的相關(guān)資料,非常淺顯易懂,有需要的小伙伴可以查看下。2015-06-06
JS實(shí)現(xiàn)將對(duì)象轉(zhuǎn)化為數(shù)組的方法分析
這篇文章主要介紹了JS實(shí)現(xiàn)將對(duì)象轉(zhuǎn)化為數(shù)組的方法,結(jié)合實(shí)例形式分析了javascript操作及轉(zhuǎn)換json數(shù)組相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-01-01
微信小程序input抖動(dòng)問(wèn)題的修復(fù)方法
這篇文章主要給大家介紹了關(guān)于微信小程序input抖動(dòng)問(wèn)題的修復(fù)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
JSON字符串和對(duì)象相互轉(zhuǎn)換實(shí)例分析
這篇文章主要介紹了JSON字符串和對(duì)象相互轉(zhuǎn)換的方法,結(jié)合實(shí)例形式分析了json格式數(shù)據(jù)的轉(zhuǎn)換方法,涉及javascript正則與字符串操作的相關(guān)技巧,需要的朋友可以參考下2016-06-06

