一、基礎(chǔ)鞏固
1.實例對象和函數(shù)對象
(1)普通對象都是實例對象,即由 new關(guān)鍵字 創(chuàng)建出來的對象。
(2)函數(shù)本身也是對象,具體體現(xiàn)為 將函數(shù)當做對象來使用。
(3)括號前面是函數(shù),點前面是對象。
function Fn(){} //Fn是函數(shù)
const fn = new Fn() //Fn是構(gòu)造函數(shù),fn是構(gòu)造函數(shù)的實例對象
console.log(Fn.prototype);//將Fn函數(shù)當做對象使用,所以Fn是函數(shù)對象
$('#app') //$代表jquery函數(shù)
$.get() // 發(fā)送ajax請求時將$函數(shù)當做對象使用,所以$是函數(shù)對象。
2.兩種類型的回調(diào)函數(shù)
(1)回調(diào)函數(shù)可以理解為 自己定義,但不是由自己調(diào)用的函數(shù)。
(2)同步回調(diào)函數(shù):立即執(zhí)行的回調(diào)函數(shù),結(jié)束后再執(zhí)行后面的內(nèi)容。不會加入異步回調(diào)隊列。
// 1.同步回調(diào)函數(shù):立即執(zhí)行的回調(diào)函數(shù),結(jié)束后再執(zhí)行后面的內(nèi)容。不會加入異步回調(diào)隊列。
// 例如:數(shù)組遍歷相關(guān)的回調(diào)函數(shù) || Promise的excutor函數(shù)
const arr = [1, 2, 3];
arr.forEach((item) => {
console.log(item);
});
console.log('遍歷后面的內(nèi)容執(zhí)行');
(3)異步回調(diào)函數(shù):回調(diào)函數(shù)不會立即執(zhí)行,會放入回調(diào)隊列在將來執(zhí)行。
// 2.異步回調(diào)函數(shù):回調(diào)函數(shù)不會立即執(zhí)行,會放入回調(diào)隊列在將來執(zhí)行。
// 例如:定時器的回調(diào)函數(shù) || Promise的成功或失敗的回調(diào)。
setTimeout(() => {
console.log('定時器的回調(diào)函數(shù)執(zhí)行');
}, 0);
console.log('定時器后面的內(nèi)容執(zhí)行');
3.錯誤處理
(1)寫函數(shù)時,對可能的錯誤手動拋出,具體處理有調(diào)用者決定。
(2)用 try{ }catch(err){ } 代碼塊包裹可能出現(xiàn)錯誤的代碼,并對錯誤進行捕獲。
function fn() {
const num = Math.round(Math.random());
if (num === 0) {
console.log('隨機數(shù)是0');
} else {
throw new Error('隨機數(shù)是1'); //throw error拋出異常,具體的處理有調(diào)用者決定
}
}
//捕獲異常
try {
fn();
} catch (error) {
alert(error);
}
二、Promise的理解和使用
1.什么是Promise
抽象表達:
Promise 是 JS 中進行異步編程的新解決方案(舊方案是單純使用回調(diào)函數(shù))
具體表達:
(1)從語法上來說: Promise 是一個 構(gòu)造函數(shù)。
(2)從功能上來說: promise 對象用來封裝一個異步操作并可以獲取其成功/失敗的結(jié)果值。
(3)Promise構(gòu)造函數(shù)(的回調(diào)函數(shù))是同步執(zhí)行的,我們將異步操作放到回調(diào)函數(shù)體內(nèi),成功和失敗時分別調(diào)用resolve和reject函數(shù),這兩個函數(shù)是異步調(diào)用的,且這兩個函數(shù)內(nèi)部會調(diào)用then方法中聲明的成功和失敗的回調(diào)函數(shù)。
2. promise 的狀態(tài)改變
(1) pending 變?yōu)?resolved
(2) pending 變?yōu)?rejected
說明:
① 只有這 2 種情況, 且一個 promise 對象 只能改變一次。
② 無論變?yōu)槌晒€是失敗, 都會有一個結(jié)果數(shù)據(jù)。
③ 成功的結(jié)果數(shù)據(jù)一般稱為 value, 失敗的結(jié)果數(shù)據(jù)一般稱為 reason。
3.Promise 的注意點
(1)Promise構(gòu)造函數(shù)本身是同步執(zhí)行的,它處理的異步操作依然是異步執(zhí)行的,但是可以通過 then()方法 或者 async、await 處理這個異步操作。
const p1 = new Promise((resolve)=> {
setTimeout(()=> {
console.log("定時器結(jié)束"); // 第二
resolve();
},1000)
})
p1.then(()=> {
console.log("then代碼"); // 第三
})
console.log("普通同步代碼"); // 第一
(2)Promise的 執(zhí)行器函數(shù) 是一個 同步回調(diào)函數(shù),即 new Promise(()=>{}) 時會立即執(zhí)行該回調(diào)函數(shù)。
(3)成功和失敗的回調(diào)是 異步回調(diào)函數(shù)。就是resolve()方法內(nèi)部 異步調(diào)用then()指定的onResolved()回調(diào)函數(shù)。(手寫Promise)
(4)直接掛載到函數(shù)對象上的方法只能被 構(gòu)造函數(shù)使用;掛載到原型對象上的方法由 實例對象使用。
Promise.all() 和 Promise.prototype.then()
三、Promise的API的使用
1.Promise構(gòu)造函數(shù)
格式:Promise (excutor) {}
(1)excutor 執(zhí)行器函數(shù): 同步執(zhí)行的回調(diào)函數(shù) (resolve, reject) => {}
(2)resolve 函數(shù): 內(nèi)部定義成功 時我們要調(diào)用的函數(shù) value => {}
(3)reject 函數(shù): 內(nèi)部定義失敗 時我們要調(diào)用的函數(shù) reason => {}
說明: excutor 會在 Promise 內(nèi)部立即同步回調(diào),異步操作會在執(zhí)行器中執(zhí)行
2. Promise.prototype.then 方法
格式:(onResolved, onRejected) => {}
(1)onResolved 函數(shù): 成功 的回調(diào)函數(shù) (value) => {}
(2)onRejected 函數(shù): 失敗 的回調(diào)函數(shù) (reason) => {}
說明:
① 指定 用于得到成功 value 的成功回調(diào)函數(shù),和用于得到失敗 reason 的失敗回調(diào)函數(shù)
② then()方法會返回一個新的 promise 對象
3. Promise.prototype.catch 方法
格式:(onRejected) => {}
(1)onRejected 函數(shù): 失敗 的回調(diào)函數(shù) (reason) => {}
說明: then()的語法糖, 相當于 then(null, onRejected)
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功的數(shù)據(jù)');
// reject('失敗的數(shù)據(jù)'); //只會執(zhí)行其中一個,因為promise的狀態(tài)只能改變一次
}, 1000);
})
.then((value) => {
console.log('操作成功,接收的數(shù)據(jù)為:' + value);
})
.catch((reason) => {
console.log('操作失敗,接收的數(shù)據(jù)為:' + reason);
});
4. Promise.resolve 方法
格式: (value) => {}
(1)參數(shù)value: 成功的數(shù)據(jù) 或 promise 對象。
說明: 返回一個 成功或失敗 的 promise 對象。
5. Promise.reject 方法
格式:(reason) => {}
(1)參數(shù)reason: 失敗的原因。
說明: 返回一個 失敗的 promise 對象。
// 生成一個成功數(shù)據(jù)為1的Promise對象:兩種方式對比
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = Promise.resolve(2);
const p3 = Promise.reject(3);
6. Promise.all 方法
格式:([promise1,promise2]) => {}
(1)參數(shù): 包含 n 個 promise 的 數(shù)組
(2)返回一個 新的 promise, 只有所有的 promise 都成功才成功, 只要有一個失敗了就直接失敗。
(3)返回的數(shù)組中的結(jié)果順序和 傳入的promise順序 一致,與執(zhí)行完成的先后順序無關(guān)。
// 接上面代碼
const pAll = Promise.all([p1, p2]);
pAll.then(
(values) => {
console.log('所有promise都成功了,結(jié)果為:', values); // 執(zhí)行成功的回調(diào)函數(shù),因為p1,p2都是成功的Promise
},
(reasons) => {
console.log('至少一個promise失敗了,結(jié)構(gòu)為:', reasons);
}
);
7.Promise.race 方法
格式:([promise1,promise2]) => {}
(1) 參數(shù): 包含 n 個 promise 的數(shù)組
(2) 返回一個 新的 promise, 第一個完成的 promise 的結(jié)果狀態(tài)就是最終的結(jié)果狀態(tài)
(3)最終結(jié)果只和 實際執(zhí)行順序有關(guān),與傳入的promise順序無關(guān)
// 接上面代碼
const pRace = Promise.race([p3, p2, p1]);
pRace.then(
(value) => {
console.log('race結(jié)果是成功的:', value);
},
(reason) => {
console.log('race結(jié)果是失敗的:', reason); //執(zhí)行失敗的回調(diào)函數(shù),因為三個promise都沒有異步操作,p3首先執(zhí)行完成
}
);
四、Promise的幾個關(guān)鍵問題
1.如何改變 promise 的狀態(tài)
(1) 調(diào)用回調(diào)函數(shù)resolve(value):如果當前是 pending 就會變?yōu)?resolved
(2) 調(diào)用回調(diào)函數(shù)reject(reason):如果當前是 pending 就會變?yōu)?rejected
(3) 拋出異常:如果當前是 pending 就會變?yōu)?rejected
const p = new Promise((resolve, reject) => {
// resolve(1); // promise變?yōu)閞esolved成功狀態(tài)
// reject(2); // promise變?yōu)閞ejected失敗狀態(tài)
// throw new Error(3); // promise變?yōu)閞ejected失敗狀態(tài),reason為拋出的內(nèi)容
throw 4;
});
2.一個 promise 指定多個成功或失敗回調(diào)函數(shù), 都會被調(diào)用
(1)當 promise 改變狀態(tài)時 會調(diào)用 所有 對應(yīng)的回調(diào)函數(shù)
const p = new Promise((resolve, reject) => {
resolve(1);
})
p.then(
(value) => {
console.log('成功:', value);
},
);
// 重新指定回調(diào)函數(shù)
p.then(
(value) => {
console.log('重新指定成功的回調(diào)函數(shù):', value);
},
);
3.改變 promise 狀態(tài)和指定回調(diào)函數(shù)誰先誰后
(1) 都有可能, 正常情況下是 先指定回調(diào)再改變狀態(tài), 但也可以先改狀態(tài)再指定回調(diào)。
(2) 如何先改狀態(tài)再指定回調(diào)?
① 在執(zhí)行器中直接調(diào)用 resolve()/reject()
② 延遲更長時間才調(diào)用 then()
(3) 什么時候才能得到數(shù)據(jù)?
① 如果先指定的回調(diào),先將回調(diào)函數(shù)保存; 當狀態(tài)發(fā)生改變時, 回調(diào)函數(shù)就會調(diào)用, 得到數(shù)據(jù)。
② 如果先改變的狀態(tài), 那當指定回調(diào)時, 回調(diào)函數(shù)就會調(diào)用, 得到數(shù)據(jù)。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1); // 后改變狀態(tài)(同時指定成功的數(shù)據(jù)),異步執(zhí)行回調(diào)函數(shù)
}, 1000);
});
//先指定回調(diào)函數(shù),保存當前指定的回調(diào)函數(shù)
p1.then((value) => {
console.log('成功:', value);
});
const p2 = new Promise((resolve, reject) => {
resolve(2); //先改變狀態(tài)(同時指定成功的數(shù)據(jù))
}).then((value) => {
// 后指定回調(diào)函數(shù),異步執(zhí)行回調(diào)函數(shù)
console.log('成功:', value);
});
4.promise.then()返回的新 promise 的結(jié)果狀態(tài)由什么決定
(1) 簡單表達: 由 then()指定的回調(diào)函數(shù) 【執(zhí)行的結(jié)果】決定,即then()返回的 promise 的結(jié)果決定。
(2) 詳細表達:
① 如果返回的是 非 promise 的任意值, 新 promise 變?yōu)?resolved成功狀態(tài), value 為返回的值。
② 如果返回的是另一個 新 promise, 此 promise 的結(jié)果就會成為新 promise 的結(jié)果。
③ 如果 拋出異常, 新 promise 變?yōu)?rejected失敗狀態(tài), reason 為拋出的異常。
new Promise((resolve, reject) => {
resolve(1);
})
.then(
(value) => {
console.log('onResolved()1:', value);
return 2; // 返回非promise,相當于返回一個成功的promise
// return Promise.resolve(3); //返回成功的promise
// return Promise.reject(4); // 返回失敗的promise
// throw 5; // 拋出異常,相當于返回一個失敗的promise
},
(reason) => {
console.log('onRejected()1:', reason);
})
.then(
(value) => {
console.log('onResolved()2:', value); // 前面then()方法最終返回的是一個成功的promise,成功結(jié)果為 2
},
(reason) => {
console.log('onRejected()2:', reason);
}
);
5.promise 串連多個操作任務(wù)
(1) promise 的 then()返回一個新的 promise, 可以看成 then()的鏈式調(diào)用。
(2) 通過 then 的鏈式調(diào)用串連多個同步 或 異步任務(wù)。
new Promise((resolve, reject) => {
//異步任務(wù)1
setTimeout(() => {
console.log('執(zhí)行任務(wù)1(異步)');
resolve(1);
}, 1000);
})
.then((value) => {
console.log('任務(wù)1的結(jié)果:', value);
console.log('執(zhí)行任務(wù)2(同步)'); //同步任務(wù)2
return 2;
})
.then((value) => {
console.log('任務(wù)2的結(jié)果:', value);
// 異步任務(wù)3,返回一個新的promise對象
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('執(zhí)行任務(wù)3(異步)');
resolve(3);
}, 1000);
});
})
.then((value) => {
console.log('任務(wù)3的結(jié)果:', value);
});
6.promise 異常穿透
(1) 當使用 promise 的 then 鏈式調(diào)用時, 可以只在 最后 指定一個失敗的回調(diào)。
(2) 前面任何操作出了異常, 都能夠傳到最后失敗的回調(diào)中進行處理。
new Promise((resolve, reject) => {
reject(1);
})
.then(
(value) => {}
// then鏈式調(diào)用時不對失敗進行處理,相當于執(zhí)行了下面代碼
// (reason) => {
// throw reason;
// return Promise.reject(reason);
// }
)
.then((value) => {})
.then((value) => {})
.catch((reason) => {
console.log('捕捉到異常:', reason);
});
7.如何中斷 promise 鏈式調(diào)用
(1) 當使用 promise 的 then 鏈式調(diào)用時, 在中間中斷, 不再調(diào)用后面的回調(diào)函數(shù)。
(2) 在回調(diào)函數(shù)中返回一個 pendding 狀態(tài)的 promise 對象。
new Promise((resolve, reject) => {
resolve(1);
})
.then((value) => {
console.log('onResolved1():', value);
return new Promise(() => {}); // 返回一個pending狀態(tài)的promise,后面的回調(diào)函數(shù)就不會被調(diào)用
})
.then((value) => {
console.log('onResolved2():', value);
});