以前學習寫的筆記,感覺還不錯,現在發出來,希望對你有幫助。如果文章對你有所啟發和幫助,可以『一鍵三連』。哦,對了,我已經脫髮了...😭😭
目錄
1. 前置知識
1.1 區別實例對象與函數對象
1.2 兩種類型的回調函數
1.3 JS的error處理
2. Promise 是什麼?
2.1 理解
2.2 Promise的狀態改變
2.3 Promise基本流程
2.4 Promise的基本使用
3. 為什麼要用Promise?
3.1 指定回調函數的方式更加靈活
3.2 支持鏈式調用,可以解決回調地獄問題
4. Promise的API說明
4.1 API 說明
4.2 Promise的幾個關鍵問題
5. async與await
1. 前置知識1.1 區別實例對象與函數對象實例對象:new 函數產生的對象, 稱為實例對象, 簡稱為對象
函數對象:將函數作為對象使用時, 簡稱為函數對象
function Fn() {}
const fn = new Fn() // fn為實例對象
Fn.bind({}) // Fn為函數對象
1.2 兩種類型的回調函數同步回調
理解:立即執行, 完全執行完了才結束, 不會放入回調隊列中例子: 數組遍歷相關的回調函數 / Promise 的 excutor 函數異步回調
例子:定時器回調 / ajax 回調 / Promise 的成功|失敗的回調const arr = [1, 2, 3]
arr.forEach(item => console.log(item)) // 同步回調, 不會放入回調隊列, 而是立即執行
console.log('forEatch()之後')
setTimeout(() => { // 異步回調, 會放入回調隊列, 所有同步執行完後才可能執行
console.log('timout 回調')
}, 0)
console.log('setTimeout 之後')
1.3 JS的error處理錯誤的類型
ReferenceError:引用的變量不存在console.log(a) // ReferenceError: a is not defined
TypeError:數據類型不正確的錯誤let b = null
console.log(b.xxx) // TypeError: Cannot read property 'xxx' of null
RangeError:數據值不在其所允許的範圍內function fn() {
fn()
}
fn() // RangeError: Maximum call stack size exceeded
SyntaxError:語法錯誤let c = """" // SyntaxError: Unexpected string錯誤處理
error 對象的結構
2. Promise 是什麼?2.1 理解抽象表達:Promise 是JS中進行異步編程的新的解決方案(舊的是誰?=> 純回調的形式)
具體表達:
從功能上來說:Promise 對象用來封裝一個異步操作並可以獲取其結果2.2 Promise的狀態改變Promise的狀態改變只有這2種:
且一個 Promise 對象只能改變一次,無論變成成功還是失敗,都會有一個結果數據,成功的結果數據一般稱為 value,失敗的結果數據一般稱為 reason。
2.3 Promise基本流程Promise基本流程2.4 Promise的基本使用示例,如果當前時間是偶數就代表成功,否則代表失敗
// 1. 創建一個新的Promise對象
const p = new Promise((resolve, reject) => { // 執行器函數,同步執行
// 2. 執行異步操作任務
setTimeout(() => {
const time = Date.now() // 如果當前時間是偶數就代表成功,否則代表失敗
// 3.1 如果成功了,調用resolve(value)
if (time % 2 === 0) {
resolve('成功的數據,value = ' + time)
} else {
// 3.2 如果失敗了,調用reject(reason)
reject('失敗的數據,reason = ' + time)
}
}, 1000);
})
p.then(value => {
// 接受得到成功的value數據,專業術語:onResolved
console.log('成功的回調', value)
}, reason => {
// 接受得到失敗的reason數據,專業術語:onRejected
console.log('失敗的回調', reason)
})
3. 為什麼要用Promise?3.1 指定回調函數的方式更加靈活舊的:回調函數必須在啟動異步任務前指定
// 成功的回調函數
function successCallback(result) {
console.log('處理成功:' + result)
}
function failureCallback(error) {
console.log('處理失敗:' + error)
}
// 使用純回調函數
createAudioFileSync(audioSettings, successCallback, failureCallback)Promise:啟動異步任務 => 返回 Promise 對象 => 給 Promise 對象綁定回調函數,甚至可以在異步任務結束後指定多個
// 使用 Promise
const promise = createAudioFileSync(audioSettings)
setTimeout(() => {
promise.then(successCallback, failureCallback)
}, 3000);
3.2 支持鏈式調用,解決回調地獄問題什麼是回調地獄?回調函數嵌套調用,外部回調函數異步執行的結果是嵌套的回掉執行條件,代碼是水平向右擴展
// 回調地獄
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult)
}, failureCallback)
}, failureCallback)
},回調地獄的缺點:不便閱讀,不便於異常處理
解決方案:Promise 鏈式調用,代碼水平向下擴展
doSomething().then(function(result) {
return doSomethingElse(result)
})
.then(function(newResult) {
return doThirdThing(newResult)
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult)
})
.catch(failureCallback)終極解決方案:async/await,用同步的寫法處理異步的操作
async function request() {
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)
}
}
4. Promise的API說明4.1 API 說明Promise 構造函數。
Promise (excutor) {},excutor 會在 Promise 內部立即同步回調,異步操作在執行器中執行
excutor 函數:執行器 (resolve, reject) => {}resolve 函數:內部定義成功時我們調用的函數 value => {}reject 函數:內部定義失敗時我們調用的函數 reason => {}Promise.prototype.then方法
(onResolved, onRejected) => {},指定用於得到成功 value 的成功回調和用於得到失敗 reason 的失敗回調返回一個新的 promise 對象
onResolved 函數:成功的回調函數 (value) => {}onRejected 函數:失敗的回調函數 (reason) => {}Promise.prototype.catch 方法
(onRejected) => {},onRejected 函數:失敗的回調函數 (reason) => {},then() 的語法糖, 相當於:then(undefined, onRejected)
Promise.resolve方法
(value) => {},value:成功的數據或 promise 對象,返回一個成功/失敗的 promise 對象
Promise.reject方法
(reason) => {},reason:失敗的原因,返回一個失敗的 promise 對象
Promise.all方法
(promises) => {},promises:包含 n 個 promise 的數組,返回一個新的 promise, 只有所有的 promise 都成功才成功, 只要有一個失敗了就直接失敗
Promise.race方法
(promises) => {},promises: 包含 n 個 promise 的數組,返回一個新的 promise, 第一個完成的 promise 的結果狀態就是最終的結果狀態
// 產生一個成功值為 1 的 Promise 對象
const p1 = new Promise((resolve, reject) => {
resolve(1)
})
// 產生一個成功值為 2 的 Promise 對象
const p2 = Promise.resolve(2)
// 產生一個失敗值為 3 的 Promise 對象
const p3 = Promise.reject(3)
p1.then(value => console.log(value))
p2.then(value => console.log(value))
p3.catch(reason => console.error(reason))
// const pAll = Promise.all([p1, p2])
const pAll = Promise.all([p1, p2, p3])
pAll.then(values => {
console.log('all onResolved()', values) // all onResolved() [ 1, 2 ]
}, reason => {
console.log('all onRejected()', reason) // all onRejected() 3
})
const race = Promise.race([p1, p2, p3])
race.then(value => {
console.log('all onResolved()', value)
}, reason => {
console.log('all onRejected()', reason)
})
4.2 Promise的幾個關鍵問題4.2.1 如何改變Promise的狀態resolve(value),如果當前是 pendding 就會變為 resolved
reject(reason),如果當前是 pendding 就會變為 rejected
拋出異常,如果當前是 pendding 就會變為 rejected
const p = new Promise((resolve, reject) => {
// resolve(1) // Promise 變為 resolved 成功狀態
// reject(2) // Promise 變為 rejected 失敗狀態
// Promise 變為 rejected 失敗狀態,reason為拋出的 error
throw new Error('我拋出的異常')
// 變為 rejected 失敗狀態,reason為拋出的 3
// throw 3
})
p.then(
value => {},
reason => { console.log('reason :', reason); }
)
4.2.2 當一個promise指定多個成功/失敗回調函數, 都會調用嗎?當 promise 改變為對應狀態時都會調用
const p = new Promise((resolve, reject) => {
// 變為 rejected 失敗狀態,reason為拋出的 3
throw 3
})
p.then(
value => {},
reason => { console.log('reason :', reason); }
)
p.then(
value => {},
reason => { console.log('reason2 :', reason); }
)
// 結果:
// reason : 3
// reason2 : 3
4.2.3 改變promise狀態和指定回調函數誰先誰後?都有可能, 正常情況下是先指定回調再改變狀態, 但也可以先改狀態再指定回調。
如何先改狀態再指定回調?
在執行器中直接調用 resolve()/reject()什麼時候才能得到數據?
如果先指定的回調, 那當狀態發生改變時, 回調函數就會調用, 得到數據如果先改變的狀態, 那當指定回調時, 回調函數就會調用, 得到數據// 常規:先指定回調函數,後改變狀態
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) // 後改變狀態(同時指定數據),異步執行回調函數
}, 1000);
}).then( // 先指定回調函數,保存當前指定的回調函數
value => {},
reason => { console.log('reason :', reason); }
)
// 先改狀態,後指定回調函數
new Promise((resolve, reject) => {
resolve(1) // 先改變狀態(同時指定數據)
}).then( // 後指定回調函數,異步執行回調函數
value => { console.log('value2:', value);},
reason => { console.log('reason2 :', reason); }
)
const p = new Promise((resolve, reject) => {
resolve(1) // 先改變狀態(同時指定數據)
})
setTimeout(() => {
p.then(
value => { console.log('value3:', value);},
reason => { console.log('reason3 :', reason); }
)
}, 1500);
4.2.4 promise.then()返回的新 promise 的結果狀態由什麼決定?簡單表達:由 then()指定的回調函數執行的結果決定
詳細表達:
如果拋出異常, 新 promise 變為 rejected, reason 為拋出的異常如果返回的是非 promise 的任意值, 新 promise 變為 resolved, value 為返回的值如果返回的是另一個新 promise, 此 promise 的結果就會成為新 promise 的結果new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved1()', value); // 1
// return 1.1 或
return Promise.resolve(1.1)
// return Promise.reject(1.1)
// throw 1.1
},
reason => {
console.log('onRejected1()', reason);
}
).then(
value => { console.log('onResolved2()', value); }, // 1.1
reason => { console.log('onRejected2()', reason) } // 1.1
)
4.2.5 promise 如何串連多個操作任務promise 的 then() 返回一個新的 promise, 可以開成 then() 的鏈式調用,通過 then 的鏈式調用串連多個同步/異步任務。
4.2.6 promise 異常傳透當使用 promise 的 then 鏈式調用時, 可以在最後指定失敗的回調,前面任何操作出了異常, 都會傳到最後失敗的回調中處理。
下面的示例代碼演示了異常傳透
new Promise((resolve, reject) => {
// resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value);
return 2
}
).then(
value => {
console.log('onResolved2()', value);
return 3
}
).then(
value => {
console.log('onResolved3()', value);
}
).catch(
reason => {
console.log('onRejected()', reason); // onRejected() 1
}
)代碼會執行 .catch 中的代碼,但實際上代碼的執行不是執行到第 3 行就直接跳轉到 catch 裡面了,而是從第一個 then 調用向下一個個的執行(逐級傳遞),但是由於我們 then 裡面沒有處理異常。在 then 裡面沒寫處理異常實際上相當於默認添加了 reason => { throw reason } 或者 reason => Promise.reject(reason):
new Promise((resolve, reject) => {
reject(1)
}).then(
value => { console.log('onResolved1()', value); },
// reason => { throw reason }
// 或者
reason => Promise.reject(reason)
)Promise的異常傳透示意圖
Promise的異常傳透4.2.7 中斷 promise 鏈當使用 promise 的 then 鏈式調用時, 在中間中斷, 不再調用後面的回調函數。
辦法: 在回調函數中返回一個 pendding 狀態的 promise 對象
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved1()', value);
return new Promise(() => {}) // 返回一個 pending 的 Promise,中斷 promise 鏈
}
).then( // 這個 then 不會執行力
value => { console.log('onResolved2()', value); }
)
5. async與awaitAsync/await 實際上只是一種基於promises的糖衣語法糖,Async/await 和 promises一樣,都是非堵塞式的,Async/await 讓異步代碼更具同步代碼風格,這也是其優勢所在。
async function 用來定義一個返回 AsyncFunction 對象的異步函數。異步函數是指通過事件循環異步執行的函數,它會通過一個隱式的 Promise 返回其結果,。如果你在代碼中使用了異步函數,就會發現它的語法和結構會更像是標準的同步函數。MDN async_functionawait 操作符用於等待一個Promise 對象。它只能在異步函數 async function 中使用。MDN await5.1 async函數async 函數的返回值為 Promise 對象,async 函數返回的 Promise 的結果由函數執行的結果決定
async function fn1() {
return 1
}
const result = fn1()
console.log(result) // Promise { 1 }在控制臺可以看見如下信息
既然是Promise對象,那麼我們用 then 來調用,並拋出錯誤,執行 onRejected() 且 reason 為錯誤信息為「我是錯誤」
async function fn1() {
// return 1
// return Promise.resolve(1)
// return Promise.reject(2)
throw '我是錯誤'
}
fn1().then(
value => { console.log('onResolved()', value) },
reason => { console.log('onRejected()', reason) } // onRejected() 我是錯誤
)
5.2 await表達式await 右側的表達式一般為 promise 對象, 但也可以是其它的值:
如果表達式是 promise 對象, await 返回的是 promise 成功的值如果表達式是其它值, 直接將此值作為 await 的返回值function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1000)
}, 1000);
})
}
function fn4() { return 6 }
async function fn3() {
// const value = await fn2() // await 右側表達式為Promise,得到的結果就是Promise成功的value
// const value = await '還可以這樣'
const value = await fn4()
console.log('value', value)
}
fn3() // value 6await 必須寫在 async 函數中, 但 async 函數中可以沒有 await,如果 await 的 Promise 失敗了, 就會拋出異常, 需要通過 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('得到失敗的結果', error)
}
}
fn3() // 得到失敗的結果 1000
5.3 Async/await 比 Promise 更優越的表現簡潔乾淨,使用async/await能省去寫多少行代碼
錯誤處理,async/wait 能用相同的結構和好用的經典 try/catch 處理同步和異步錯誤,錯誤堆棧能指出包含錯誤的函數。
調試,async/await 的一個極大優勢是它更容易調試,使用async/ await則無需過多箭頭函數,並且能像正常的同步調用一樣直接跨過await調用。