1楚昭、準備
1.1栖袋、區(qū)別實例對象與函數(shù)對象
1.實例對象: new函數(shù)產(chǎn)生的對象,稱為實例對象抚太,簡稱為對象
2.函數(shù)對象:將函數(shù)作為對象使用時塘幅,簡稱為函數(shù)對象
function Fn(){//Fn函數(shù)
};
let fn = new Fn();//Fn是構(gòu)造函數(shù)昔案、fn是實例對象(簡稱為對象)
console.log(Fn.prototype);//Fn是函數(shù)對象
Fn.bind({});//Fn是函數(shù)對象
$("#test");//jQuery 函數(shù)
$.get("/test");//jQuery函數(shù)對象
1.2、兩種類型的回調(diào)函數(shù)
1.2.1晌块、同步回調(diào)
1爱沟、理解: 立即執(zhí)行,完全執(zhí)行完了才結(jié)束,不會放入回調(diào)隊列中
2匆背、例子: 數(shù)組遍歷相關(guān)的回調(diào)函數(shù)呼伸、Promise的 excutor函數(shù)
例一、數(shù)組遍歷相關(guān)的回調(diào)函數(shù)
let arr = ["a","b","c"];
arr.forEach(r => { //遍歷回調(diào)函數(shù)
console.log("11111");
});
console.log("222222222");
例二钝尸、Promise的 excutor函數(shù)
new Promise(resolve => {
console.log("11111");
})
console.log("222222");
1.2.2括享、異步回調(diào)
1、理解: 不會立即執(zhí)行珍促,會放入回調(diào)隊列中將來執(zhí)行
2铃辖、例子: 定時器回調(diào) / ajax回調(diào) / Promise的成功|失敗的回調(diào)
例一、定時器回調(diào)
setTimeout(() =>{
console.log("11111");
}, 0);
console.log("222222222");
例二猪叙、Promise的成功|失敗的回調(diào)
new Promise(resolve => {
resolve("")
}).then(res => {
console.log("1111");
})
console.log("222222");
1.3娇斩、JS 的 error 處理
1.3.1、錯誤的類型
1穴翩、Error: 所有錯誤的父類型
2犬第、ReferenceError: 引用的變量不存在
3、TypeError: 數(shù)據(jù)類型不正確的錯誤
4芒帕、RangeError: 數(shù)據(jù)值不在其所允許的范圍內(nèi)
5.歉嗓、SyntaxError: 語法錯誤
// 1、Error: 所有錯誤的父類型
// 2背蟆、ReferenceError: 引用的變量不存在
console.log(a);//ReferenceError: a is not defined
console.log("----------");//沒有捕獲 error,下面的代碼不會被執(zhí)行(這行代碼沒有被執(zhí)行)
// 3带膀、TypeError: 數(shù)據(jù)類型不正確的錯誤
let b;
// console.log(b.xxx);//TypeError: Cannot read property 'xxx' of null
b = {};
b.xxx();//TypeError: b.xxx is not a function
// 4志珍、RangeError: 數(shù)據(jù)值不在其所允許的范圍內(nèi)
function fn(){
fn();
};
fn();//RangeError: Maximum call stack size exceeded
// 5.、SyntaxError: 語法錯誤
const c = """";//SyntaxError: Unexpected string
1.3.2本砰、錯誤處理
1休弃、捕獲錯誤:try ... catch
2霹琼、拋出錯誤:throw error
//1、捕獲錯誤:try ... catch
try{
let b;
console.log(b.xxx );
} catch (error){
console.log(error.message);
console.log(error.stack);
}
console.log("出錯之后洁灵,通過捕獲錯誤莺琳,程序能繼續(xù)處理");
//2还棱、拋出錯誤:throw error
function something(){
if(Date.now()%2 == 1){
console.log("當前時間為奇數(shù),可以執(zhí)行任務");
something()
}else{//如果時間是偶數(shù)拋出異常,由調(diào)用來處理
throw new Error("當前時間為偶數(shù),無法執(zhí)行任務");
}
}
try{
something()
} catch(error){
alert(error.message)
}
2惭等、Promise的理解與使用
2.1珍手、Promise是什么?
2.1.1、理解
抽象表達
1琳要、promise 是ES6中提供的一個異步編程的新的解決方案(舊的是誰?)
具體表達
1寡具、從語法上來說: Promise是一個構(gòu)造函數(shù)
2、從功能上來說: promise對象用來封裝一個異步操作稚补,并可以獲取其結(jié)果
2.1.2童叠、Promise 狀態(tài)與狀態(tài)的改變
Promise對象有三種狀態(tài):
- 待定(pending): 初始狀態(tài),既沒有被兌現(xiàn)课幕,也沒有被拒絕厦坛。
- 已兌現(xiàn)(fulfilled): 意味著操作成功完成。
- 已拒絕(rejected): 意味著操作失敗乍惊。
這三種狀態(tài)的變化途徑只有2種:
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
2.1.3莉撇、Promise 的基本流程
MDN Promise基本流程圖
注意: 如果一個 promise 已經(jīng)被兌現(xiàn)(fulfilled)或被拒絕(rejected)呢蛤,那么我們也可以說它處于已敲定(settled)狀態(tài)。您還會聽到一個經(jīng)常跟 promise 一起使用的術(shù)語:已決議(resolved)稼钩,它表示 promise 已經(jīng)處于已敲定(settled)狀態(tài)顾稀,或者為了匹配另一個 promise 的狀態(tài)被"鎖定"了。Domenic Denicola 的 States and fates 中有更多關(guān)于 promise 術(shù)語的細節(jié)可以供您參考坝撑。
簡化的 Promise基本流程圖
2.1.4静秆、Promise 的基本使用
// 1、創(chuàng)建一個新的 Promise 對象
const ret = new Promise((resolve,reject) => {//執(zhí)行器函數(shù)
// 2巡李、執(zhí)行異步操作任務
setTimeout(() => {
const time = Date.now();//如果當前時間是偶數(shù)就代表成功,否則代表失敗
// 3.1抚笔、如果成功了,調(diào)用resolve(value)
if(time % 2 == 0){
resolve("成功的數(shù)據(jù)侨拦,time=" + time);
}else{
// 3.2殊橙、如果失敗了,調(diào)用reject(reason)
reject("失敗的數(shù)據(jù)狱从,time=" + time);
}
})
})
// ret.then(
// value => {//接收得到成功的 value 數(shù)據(jù)
// console.log("成功的回調(diào)-----------", value);
// },
// reason => {//接收得到失敗的 reason 數(shù)據(jù)
// console.log("失敗的回調(diào)-----------", reason);
// }
// )
// promise簡化了對error的處理膨蛮,上面的代碼我們也可以這樣寫:
ret.then(//接收得到成功的 value 數(shù)據(jù)
value => {
console.log("成功的回調(diào)-----------", value);
}
).catch(//接收得到失敗的 reason 數(shù)據(jù)
reason => {
console.log("失敗的回調(diào)-----------", reason);
}
)
2.2、為什么要使用 Promise季研?
1. 指定異步回調(diào)函數(shù)的方式更加靈活:
舊的: 必須在啟動異步任務前指定
promise: 啟動異步任務 => 返回promie對象 => 給promise對象綁定回調(diào)函數(shù)(甚至可以在異步任務結(jié)束后指定)
// 舊的指定回調(diào)函數(shù)
function successBcak(){
console.log("成功的回調(diào)");
}
function failBcak(){
console.log("失敗的回調(diào)");
}
function person(age, successBcak, failBcak){
setTimeout(() => {
if(age > 0 && age < 100){
successBcak();
}else{
failBcak();
}
},1000)
}
console.log("aaaaaa");
person(18, successBcak, failBcak);//在調(diào)用前必須指定回調(diào)函數(shù)敞葛,而 Promise不需要
console.log("bbbbbbb");
// Promise
const p = new Promise((resolve, reject) => {
console.log('執(zhí)行 executor同步函數(shù)')
let time = Date.now();
setTimeout(() => {//Promise可以先執(zhí)行異步任務,再指定回調(diào)函數(shù)
if (time % 2 === 0) {
console.log("resolve")
resolve(time)
} else {
console.log("reject")
reject(time)
}
}, 2000)
})
setTimeout(() => {
p.then(value => {
console.log('value', value)
}, reason => {
console.log('reason', reason)
})
}, 3000)
console.log("aaaaaaaa");
2与涡、 支持鏈式調(diào)用, 可以解決回調(diào)地獄問題
什么是回調(diào)地獄? 回調(diào)函數(shù)嵌套調(diào)用, 外部回調(diào)函數(shù)異步執(zhí)行的結(jié)果是嵌套的回調(diào)函數(shù)執(zhí)行的條件
回調(diào)地獄的缺點? 不便于閱讀 / 不便于異常處理
解決方案? promise鏈式調(diào)用
終極解決方案? async/await
回調(diào)地獄
$.ajax({
url:"查詢用戶",
success:function(res){
console.log(res)
// 將數(shù)據(jù)渲染到頁面上
$.ajax({
url: "查詢到課程",
success:function(res){
// 獲取到了課程內(nèi)容的數(shù)據(jù),渲染到頁面上
$.ajax({})
}
})
}
})
promise 處理回調(diào)地獄
new Promise(function(resolve, reject){
$.ajax({
url:"查詢用戶",
success:function(res){
resolve(res)
},
error:function(err){
reject(err)
}
})
}).then(res=>{
console.log(res)
return new Promise(function(resolve,rejeect){
$.ajax({
url: "查詢到課程",
success:function(res1){
// 獲取到了文章詳細內(nèi)容的數(shù)據(jù),渲染到頁面上
resolve(res1)
},
error:function(err){
reject(err)
}
})
})
}).then(res=>{
console.log(res)
return new Promise(function(resolve,rejeect){
$.ajax({
url: "查詢到分數(shù)",
success:function(res1){
// 獲取到了文章詳細內(nèi)容的數(shù)據(jù),渲染到頁面上
resolve(res1)
},
error:function(err){
reject(err)
}
})
})
}).then(res=>{
console.log(res)
})
封裝一下 Promise
function get(url) {
return new Promise((resolve, reject) => {
$.ajax({
url: url,
success: function (data) {
resolve(data);
},
error: function (err) {
reject(err)
}
})
});
}
//調(diào)用封裝后的方法
get("查詢用戶")
.then((data) => {
console.log("用戶查詢成功~~~:", data)
return get("查詢到課程);
})
.then((data) => {
console.log("課程查詢成功~~~:", data)
return get("查詢到分數(shù));
})
.then((data) => {
console.log("課程成績查詢成功~~~:", data)
}).catch((err) => {
console.log("出現(xiàn)異常", err)
});
async function foo() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
}
2.3惹谐、如何使用 Promise持偏?(常用的 Promise API)
Promise.all:(promises) =>{}
promises:包含n個promise的數(shù)組
重點:返回一個新的promise,只有所有的promise都成功才成功氨肌,只要有一個失敗了就直接失敗
Promise.all([p1,p2,......]).then(values => {
console.log(values);
}).catch(error => {
consloe.log(error);
})
開發(fā)中常用的Promise.all場景鸿秆,獲取兩個接口成功的返回值后,再執(zhí)行下面的操作
function p1(){
return new Promise((resolve) => {
setTimeout(() => {//接口一
resolve("p1")
},2000)
})
}
function p2(){
return new Promise((resolve,reject) => {
setTimeout(() => {//接口二
resolve("p2")
},1000)
})
}
Promise.all([p1(),p2()]).then(values =>{
console.log(values)
}).catch(error =>{
console.log("err",error);
})
Promise.race: (promises) =>{}
promises:包含n個promise的數(shù)組
重點:返回一個新的promise怎囚,說明:返回一個新的promise卿叽,第一個完成的promise的結(jié)果狀態(tài)就是最終的結(jié)果狀態(tài)
Promise.race([p1,p2,......]).then(values => {
console.log(values);
}).catch(error => {
consloe.log(error);
})
function p1(){
return new Promise((resolve) => {
setTimeout(() => {
resolve("p1")
},1000)
})
}
function p2(){
return new Promise((resolve,reject) => {
setTimeout(() => {
reject("p2")
},2000)
})
}
Promise.race([p1(),p2()]).then(values =>{
console.log(values)
}).catch(error =>{
console.log("err",error);
})
3、async 與 await
MDN文檔:
async 函數(shù):https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function
await:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await
async await 的作用
簡化promise對象的使用:不用再使用then()來指定成功/失敗的回調(diào)函數(shù)以同步編碼(沿有回凋函數(shù)了)方式實現(xiàn)異步流程
3.1桩了、async 函數(shù)
1附帽、函數(shù)的返回值為promise對象
2.、promise對象的結(jié)果由async函數(shù)執(zhí)行的返回值決定
// async 函數(shù)的返回值是一個promise對象
async function fn1(){
// 同步
// return 1;
// throw 2;
// return Promise.resolve(3);
// return Promise.reject(4);
// 異步
return new Promise((resolve,reject) =>{
setTimeout(()=> {
resolve(5)
},2000)
})
}
let ret1 = fn1();
console.log(ret1);
// async 函數(shù)的返回值是一個promise對象井誉,所以需要通過.then(()=>{})獲取函數(shù)的返回值
ret1.then(res => {
console.log("then", res)
}).catch(err =>{
console.log("catch", err)
})
3.2蕉扮、 await表達式
await 操作符用于等待一個 Promise 對象。它只能在異步函數(shù) Promise 中使用
1颗圣、await右側(cè)的表達式一般為 promise 對象喳钟,但也可以是其它的值
2、如果表達式是promise對象, await 返回的是 promise 成功的值(所以獲取 promise 失敗的返回值在岂,會使用try{} catch (error) {})
function fn2(){
return new Promise((resolve,reject) => {
setTimeout(()=> {
// resolve(11111)
reject(22222)
},2000)
})
}
function fn3(){
return 444444;
}
async function getResult(){
try {
let val = await fn2();//await右側(cè)表達為promise對象奔则,得到的結(jié)果就是promise對象成功的value
console.log("getResult",val);
} catch (error) {
console.log("失敗的結(jié)果",error);
}
// let val2 = await fn3();///await右側(cè)表達不是promise對象,得到的結(jié)果就是它本身
// console.log("val2",val2)
}
getResult();
async與await配合使用
function fn2(){
return new Promise((resolve,reject) => {
setTimeout(()=> {
resolve(11111)
// reject(22222)
},2000)
})
}
async function fn3(){//async 函數(shù)的返回值是一個promise對象
return 444444;
}
async function getResult(){
try {
let val = await fn2();//await右側(cè)表達為promise對象,得到的結(jié)果就是promise對象成功的value
let val2 = await fn3();
console.log("getResult",val);
console.log("getResult",val2);
} catch (error) {
console.log("失敗的結(jié)果",error);
}
}
getResult();