建設(shè)政府網(wǎng)站網(wǎng)站建設(shè)是干什么的
💻 【ES6】掌握Promise和利用Promise封裝ajax 🏠專欄:JavaScript
👀個人主頁:繁星學(xué)編程🍁
🧑個人簡介:一個不斷提高自我的平凡人🚀
🔊分享方向:目前主攻前端,其他知識也會階段性分享🍀
👊格言:??沒有走不通的路,只有不敢走的人!??
👉讓我們一起進步,一起成為更好的自己!!!🎁
文章目錄
- 【ES6】掌握Promise和利用Promise封裝ajax
- 一. 概念
- 二. Promise的靜態(tài)方法
- (1) all
- (2) race
- (3) allSettled (ES2020新增)
- (4) any
- (5) Promise的其他方法
- 三. JavaScript容錯處理
- try...catch...
- 四. 利用promise封裝ajax
- 五. async和await
【ES6】掌握Promise和利用Promise封裝ajax
一. 概念
Promise是ES6 新增的特性
目的:用來解決異步解決回調(diào)地獄問題
利用Promise類創(chuàng)建promise實例對象,類的參數(shù)是回調(diào)函數(shù),回調(diào)函數(shù)又有兩個參數(shù)(一般約定為resolve(成功)和reject(失敗))
Promise 的基本語法
const 實例對象 = new Promise(回調(diào)函數(shù))
eg:
const p = new Promise(function(resolve,reject){});
回調(diào)函數(shù)中的參數(shù):
- resolve:成功的回調(diào)
- reject:失敗的回調(diào)
Promise 的三個狀態(tài):
- 持續(xù):pending
- 成功:fulfilled
- 失敗:rejected
Promise 的兩種轉(zhuǎn)換(三種狀態(tài)變成兩種可能):
- 從持續(xù)狀態(tài)轉(zhuǎn)換到成功
- 從持續(xù)狀態(tài)轉(zhuǎn)換到失敗
Promise 的實例原型方法:
- then(函數(shù)):成功狀態(tài)的時候執(zhí)行的方法
- catch(函數(shù)):失敗狀態(tài)的時候執(zhí)行的方法
- finally(函數(shù)) :結(jié)束時執(zhí)行的方法,不管狀態(tài)是成功還是失敗,finally都會執(zhí)行
Promise 的調(diào)用方式 (鏈?zhǔn)秸{(diào)用)
- 當(dāng)在第一個then里面返回 一個新的promise對象的時候
- 可以在第一個then后面繼續(xù)第二個then
const p = new Promise(function (resolve, reject) {const time = Math.random() * 2000 + 2000console.log(time);setTimeout(() => {if (time > 3000) {console.log('成功');resolve("success");} else {console.log('失敗');reject("reject");}}, 2000);
});
// 成功狀態(tài)的時候執(zhí)行
p.then(function (sa) {console.log(sa);
})
// 失敗狀態(tài)的時候執(zhí)行
p.catch(function (sc) {console.log(sc);
});
使用函數(shù)簡單封裝Promise代碼
function fz() {const p = new Promise(function (resolve, reject) {const time = Math.random() * 2000 + 2000console.log(time);setTimeout(() => {if (time > 3000) {resolve("success")} else {reject("reject");}}, 2000);});return p;
}
fz().then(function (num) {console.log("1成功", num);
}).then(function (num) {console.log("2成功", num);
}).then(function (num) {console.log("3成功", num);
}).catch(function (num) {console.log('失敗', num);
}).finally(function () {console.log('無論最終是成功還是失敗,都會執(zhí)行');
})
二. Promise的靜態(tài)方法
(1) all
語法:Promise.all([多個 Promise])
作用:用于將多個 Promise 實例,包裝成一個新的 Promise 實例,接受一個數(shù)組作為參數(shù),只有數(shù)組里面的每個狀態(tài)都變成resolve,則新的 Promise 實例狀態(tài)才會變成resolve。
function fz1() {const p = new Promise(function (resolve, reject) {const time = Math.random() * 2000 + 2000;console.log(time, '第一個請求');setTimeout(() => {if (time > 3000) {resolve({ code: 1, message: '成功' })} else {if (time < 2500) {resolve({ code: 0, message: '失敗1' })} else {resolve({ code: 2, message: '失敗2' })}}}, time)})return p;
}
function fz2() {const p = new Promise(function (resolve, reject) {const time = Math.random() * 2000 + 2000;console.log(time, '第二個請求');setTimeout(() => {if (time > 3000) {resolve({ code: 1, message: '成功' })} else {if (time < 2500) {resolve({ code: 0, message: '失敗1' })} else {resolve({ code: 2, message: '失敗2' })}}}, time)})return p;
}
function fz3() {const p = new Promise(function (resolve, reject) {const time = Math.random() * 2000 + 2000;console.log(time, '第三個請求');setTimeout(() => {if (time > 3000) {resolve({ code: 1, message: '成功' })} else {if (time < 2500) {resolve({ code: 0, message: '失敗1' })} else {resolve({ code: 2, message: '失敗2' })}}}, time)})return p;
}Promise.all([fz1(), fz2(), fz3()]).then(res => console.log('成功', res)).catch(err => console.log('失敗', err))
(2) race
語法:Promise.race([多個promise])
作用:將多個 Promise 實例,包裝成一個新的 Promise 實例,接受一個數(shù)組作為參數(shù),只要其中有一個實例率先改變狀態(tài),則整個的狀態(tài)就跟著改變。
function fz1() {const p = new Promise(function (resolve, reject) {const time = Math.random() * 2000 + 2000;console.log(time, '第一個請求');setTimeout(() => {if (time > 3000) {resolve({ code: 1, message: '成功' })} else {if (time < 2500) {resolve({ code: 0, message: '失敗1' })} else {resolve({ code: 2, message: '失敗2' })}}}, time)})return p;
}
function fz2() {const p = new Promise(function (resolve, reject) {const time = Math.random() * 2000 + 2000;console.log(time, '第二個請求');setTimeout(() => {if (time > 3000) {resolve({ code: 1, message: '成功' })} else {if (time < 2500) {resolve({ code: 0, message: '失敗1' })} else {resolve({ code: 2, message: '失敗2' })}}}, time)})return p;
}
function fz3() {const p = new Promise(function (resolve, reject) {const time = Math.random() * 2000 + 2000;console.log(time, '第三個請求');setTimeout(() => {if (time > 3000) {resolve({ code: 1, message: '成功' })} else {if (time < 2500) {resolve({ code: 0, message: '失敗1' })} else {resolve({ code: 2, message: '失敗2' })}}}, time)})return p;
}
Promise.race([fz1(), fz2(), fz3()]).then(res => console.log('成功', res)).catch(err => console.log('失敗', err))
(3) allSettled (ES2020新增)
語法:Promise.allSettle([多個promise])
作用:方法接受一個數(shù)組作為參數(shù),數(shù)組的每個成員都是一個 Promise 對象,并返回一個新的 Promise 對象。只有等到參數(shù)數(shù)組的所有 Promise 對象都發(fā)生狀態(tài)變更(不管是fulfilled還是rejected),返回的 Promise 對象才會發(fā)生狀態(tài)變更。
簡單理解就是:不管是成功還是失敗,都會觸發(fā),并會把結(jié)果用數(shù)組的形式返回,數(shù)組里面放著每一個promise的結(jié)果
let p1 = new Promise((resolve, reject) => {resolve('1')
});
let p2 = new Promise((resolve, reject) => {reject('2')
});
let p3 = new Promise((resolve, reject) => {reject('3')
});let allSettled = Promise.allSettled([p1, p2, p3]);
allSettled.then((res) => {console.log('then:', res); // 返回數(shù)組,數(shù)組里面有三個對象,對象里面區(qū)分成功和失敗的值,狀態(tài)是正常的。//status:resolve value / status:reject reasonconsole.log(res[0].status); // fulfilledconsole.log(res[0].value); // 1console.log(res[1].status); // rejectedconsole.log(res[1].reason); // 2
});
(4) any
語法:Promise.any([多個promise])
作用:該方法接受一組 Promise 實例作為參數(shù),包裝成一個新的 Promise 實例返回。Promise.any()不會因為某個 Promise 變成rejected狀態(tài)而結(jié)束,必須等到所有參數(shù) Promise 變成rejected狀態(tài)才會結(jié)束。
let p1 = new Promise((resolve, reject) => {reject('1')// resolve('1')
});
let p2 = new Promise((resolve, reject) => {// reject('2')resolve('2')
});
let p3 = new Promise((resolve, reject) => {reject('3')
});// 解析:
// 如果遇到成功的請求,通過then返回resolve傳遞給then對應(yīng)的值,如果都是失敗的狀態(tài),最終走catch輸出默認(rèn)的字符( All promises were rejected),執(zhí)行catch里面的代碼,無法獲取reject里面?zhèn)魅氲闹怠?/span>
let any = Promise.any([p1, p2, p3]);
any.then(res => {console.log('then:', res);
}).catch(msg => {console.log('catch代碼'); console.log(msg); //默認(rèn)的字符All promises were rejected
});
(5) Promise的其他方法
以下兩個方法主要是開發(fā)人員調(diào)試用的
-
resolve()強行把promise的狀態(tài)改為成功
let num = 10; Promise.resolve(num).then(res => console.log(res)) console.log(1);
-
reject()強行把Promise的狀態(tài)改為失敗
let num = 10; Promise.reject(num).then().catch(res => {console.log(res) // 10 }); console.log(1); // 1
三. JavaScript容錯處理
因為js代碼如果出現(xiàn)錯誤,立刻停止,后續(xù)的代碼不會執(zhí)行。
console.log(a); // referenceError:a is not defined
console.log(1);
所以需要采用方法使js代碼出現(xiàn)錯誤時,不會影響到后續(xù)代碼
try…catch…
語法1:
try {執(zhí)行代碼} catch (err) {執(zhí)行代碼}
使用:
首先執(zhí)行try里面的代碼,如果不報錯,catch不執(zhí)行
如果報錯,不會拋出錯誤,而是執(zhí)行catch,把錯誤信息給到err。
try {console.log(a);
} catch (e) { //e:try里面如果存在錯誤,e就是try里面的錯誤信息console.log(e); // referenceError:a is not defined console.log('這里是catch的輸出'); // 這里是catch的輸出
}console.log('后面后續(xù)的代碼輸出'); // 后面后續(xù)的代碼輸出
console.log('后面后續(xù)的代碼輸出'); // 后面后續(xù)的代碼輸出
語法2:
try {執(zhí)行代碼} catch (err) {執(zhí)行代碼} finally{執(zhí)行代碼}
注:finally里面的語法不管是執(zhí)行try還是catch,finally里面的代碼一定會執(zhí)行。
try {console.log('a');
} catch (e) { //e:try里面如果存在錯誤,e就是try里面的錯誤信息console.log(e); // referenceError:a is not definedconsole.log('這里是catch的輸出');
} finally { //收尾的工作,不再使用的對象,變量,設(shè)置nullconsole.log('finally一定會執(zhí)行');
}
/*
輸出結(jié)果:afinally一定會執(zhí)行
*/
應(yīng)用場景-案例
隨機生成一個數(shù)字,添加判斷條件,最終輸出成功或失敗結(jié)果
function fz() {const p = new Promise(function (resolve, reject) {const time = Math.random() * 2000 + 2000console.log(time);setTimeout(() => {if (time > 2500) {resolve("success")} else {reject("reject");}}, 2000);});return p;
}
async function cs() {try {const res1 = await fz();console.log(res1);} catch (err) {const res2 = await fz();console.log(res2);}
}
cs()
四. 利用promise封裝ajax
封裝前的分析:
/*封裝ajax function ajax(options){}調(diào)用封裝的ajaxajax({})分析封裝方案1. 回調(diào)函數(shù)方式- 將來使用的時候,可能會出現(xiàn)回調(diào)地獄2. promise - 后面 then/catch- 也可以用async/await參數(shù)1. 請求地址 url 必填2. 請求方式 method,選填(只允許 get post),要么不傳,默認(rèn)值 get3. 是否異步 async,選填 默認(rèn)true4. 參數(shù) data 選填,默認(rèn)是'', (允許傳查詢字符串 和 對象)5. 請求頭headers 選填,默認(rèn){content-type:'application/x-www-form-urlencoded'}6. 是否解析響應(yīng)的參數(shù) dataType, 選填 默認(rèn)是string, 選填 json
*/
封裝代碼:
// 對象格式轉(zhuǎn)查詢字符串格式
function queryString(obj) {let str = ''for (let k in obj) {str += `&${k}=${obj[k]}`}return str.slice(1)
}// 利用閉包保存url基準(zhǔn)地址
function createAjax(url) {// 設(shè)置一個變量作為基準(zhǔn)地址,并長期保存這個變量let baseUrl = url// 封裝ajax函數(shù)function ajax(options = {}) {// 1.驗證參數(shù)格式// 1-1.驗證url格式:必填if (!options.url) {// 沒填拋出錯誤:'url必填'throw new Error('url必填')}// 1-2.驗證method:要么不寫,要么get或postif (!(options.method === undefined || /^(get|post)$/i.test(options.method))) {throw new Error('請求方式支持get 和 post')}// 1-3.驗證async:要么不寫,要么true/false ===> 參數(shù)是布爾類型if (!(options.async === undefined || typeof options.async === 'boolean')) {throw new Error('async需要一個布爾值')}// 1-4.驗證data:要么不寫,支持字符串格式/對象格式if (!(options.data === undefined || typeof options.data === 'string' || Object.prototype.toString.call(options.data) === '[object Object]')) {throw new Error('data需要一個字符串格式或者對象格式')}// 1-5.驗證headers:要么不寫,要么是對象格式if (!(options.headers === undefined || Object.prototype.toString.call(options.headers) === '[object Object]')) {throw new Error('headers需要一個對象格式')}// 1-6.驗證dataType參數(shù):要么不寫,要么是string 或者 json if (!(options.dataType === undefined || /^(string|json)$/.test(options.dataType))) {throw new Error('dataType 只能寫 string json')}// 2.準(zhǔn)備一個默認(rèn)值const _default = {// 修改請求地址:基準(zhǔn)地址+options.url// eg:options.url = /test/firsturl: baseUrl + options.url,// method只有三個選擇(沒寫,get,post)method: options.method || 'get',// async只有三個選擇(沒寫,true,false)并且沒寫:默認(rèn)值是true// 一個ES2020新的操作符 ??// ?? 空值運算符: 只有左邊的是undefined或者null, 才使用右邊的async: options.async ?? true,// data只能有三個選擇:字符串、對象、undefined,默認(rèn)值是''data: options.data || '',// headers只能是 undefined或者對象,默認(rèn)是{'content-type': 'application/x-www-form-urlencoded'}headers: { 'content-type': 'application/x-www-form-urlencoded', ...options.headers },// 是否解析響應(yīng)的參數(shù) dataType, (選填) 默認(rèn)是string, (選填) jsondataType: options.dataType || 'string'}// 2-1.data可能是對象,如果是對象,需要轉(zhuǎn)為查詢字符串格式if (typeof _default.data === 'object') {_default.data = queryString(_default.data)}// 2-2.請求方式是get并且data有值,url需要拼接上剛剛轉(zhuǎn)換的dataif (/^get$/i.test(_default.method) && _default.data) {_default.url = _default.url + '?' + _default.data}// 3 創(chuàng)建ajax對象 按照promise方式const p = new Promise(function (resolve, reject) {const xhr = new XMLHttpRequest()xhr.open(_default.method, _default.url, false)xhr.onload = function () {// 這里的xhr.readyState必然是4,onload是一個請求完成的事件// 如果請求成功,xhr狀態(tài)碼是4,http狀態(tài)碼是200-299 成功// 需求, 根據(jù)業(yè)務(wù)進一步封裝,如果后端返回的內(nèi)容中,code為1,成功,其他失敗if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {let res = xhr.responseText// 根據(jù)服務(wù)端返回的內(nèi)容判斷// 首先代碼進入到這里,http請求成功,可以成功從服務(wù)端拿到響應(yīng)內(nèi)容// 響應(yīng)內(nèi)容分兩種情況,// 一個是正確的// 一個是業(yè)務(wù)錯誤 (參數(shù)不正確,代碼錯誤,其他....)// 根據(jù)后端返回的內(nèi)容判斷, code:1 正確的 成功// code 不是1 錯誤(用戶密碼錯誤,登錄時間過期 )try {res = JSON.parse(res)}catch (err) {return}if (res.code === 1) {// 正確的:成功返回正確的數(shù)據(jù) resolve({ code: 1, message: "成功", data: _default.dataType === 'string' ? xhr.responseText : res })} else {// 錯誤:包括各種錯誤 resolve({ code: 0, message: '業(yè)務(wù)失敗', err: res })}} else {// 請求不成功 -- 進入這里的代碼必然是http狀態(tài)碼不是200-299的情況let res = xhr.responseTextresolve({ code: 0, message: '網(wǎng)絡(luò)請求失敗', err: res })}}// 如果post if (/^post$/i.test(_default.method)) {xhr.setRequestHeader('content-type', _default.headers['content-type'])}// 如果有token if (_default.headers.authorization) {xhr.setRequestHeader('authorization', _default.headers['authorization'])}// 如果是post 需要發(fā)送dataif (/^post$/i.test(_default.method)) {xhr.send(_default.data)} else {xhr.send()}})// 把promise對象返回return p}// 返回ajax函數(shù)return ajax
}
// 設(shè)置url基準(zhǔn)地址
let ajax = createAjax('http://localhost:8888')
測試:
// 1.利用Promise的then()方法測試封裝的ajax
// ajax({
// url: '/test/first',
// }).then(res => {
// console.log(res);
// })// 2.利用async await 測試封裝的ajax
async function fn() {const res = await ajax({ url: '/goods/list', dataType: 'json' })console.log(res);
}
fn()
注:測試前需要開啟本地服務(wù)器
本地服務(wù)器下載地址:Ajax案例測試本地服務(wù)器-Node.js文檔類資源-CSDN文庫
五. async和await
-
async其實就是promise的語法糖。函數(shù)前面加一個async,函數(shù)里面異步操作的方法前加一個await關(guān)鍵字。
顧名思義,await就是讓你等一下,執(zhí)行完了再繼續(xù)往下走。
注意:await只能在async函數(shù)中執(zhí)行,否則會報錯。
目的:讓異步代碼像同步代碼一樣執(zhí)行。
-
async:異步的意思,作用是申明一個異步函數(shù),函數(shù)的返回值是promise 對象
async function fn1() { } console.log(fn1()); //async函數(shù)隱式返回一個promise對象
-
await:等待
await是async+wait的結(jié)合 即異步等待,async和await 二者必須是結(jié)合著使用。
注:匿名函數(shù)也可以使用async和await
!async function () {let h = await new Promise((resolve, reject) => {resolve('hello')});console.log(h); //hello 證明await就是promise里面then的語法糖。 }();
結(jié)束語:
希望對您有一點點幫助,如有錯誤歡迎小伙伴指正。
👍點贊:您的贊賞是我前進的動力!
?收藏:您的支持我是創(chuàng)作的源泉!
?評論:您的建議是我改進的良藥!
一起加油!!!💪💪💪