Promise 對象
Promise
對象用于解決Javascript
中的地獄回調問題,有效的減少了程序回調的嵌套調用。
創建
如果要創建一個Promise
對象,最簡單的方法就是直接new
一個。但是,如果深入學習,會發現使用Promise
下的靜態方法Promise.resolve()
也能創建一個Promise
對象:
// 創建方法一
new Promise((resolve, reject) => {// 此處做一個異步的事情
});// 創建方法二
Promise.resolve(p) // p 可以是一個Promise,也可以是一個普通的數值。
使用方法二創建Promise
時,可以傳入一個普通的值,或一個Promise
對象。最后都會作為一個Promise
返回出來。如果傳入的是一個普通的值,產生的Promise
的值就會將這個值傳入resolve
方法發送給下一個then
。
使用
對于Promise
對象的使用,參考下方的案例,對于Promise
的使用,理解返回值、參數、兩個回調之間的關系后會有一定的幫助。
第二種寫法的區別主要在于直接在第一次定義
Promise
的同時把下一次then
中的回調也順便地寫好了。
// 案例一
const n = 6
const p = new Promise((resolve, reject) => {setTimeout(() => {if (n > 5) {resolve(n)} else {reject('必須大于 5!')}}, 1000)
})
p.then((v) => {console.log(v)},(e) => {console.log(e)}
)
// 案例二
const pFn = function() {return Promise.resolve('解決!').then(v => {console.log('接收到', v);})
}
const p = pFn()
Promise.all() 方法
該方法用于一次性執行全部傳入的[p1, p2, p3]
對象,當全部執行成功后才會進入到第一個執行成功的then
方法中。其中,任何一個失敗了則會進入到then
的失敗回調中。
// 語法演示的偽代碼
Promise.all([p1, p2, p3]).then((v) => {// 所有請求成功后的操作步驟},(e) => {// 某一個請求失敗后的操作步驟}
)// 演示案例
function p(n) {return new Promise((resolve, reject) => {setTimeout(() => {if (n > 0) {resolve(n)} else {reject('不能小于 0!')}}, 1000)})
}
Promise.all([p(5), p(6), p(7)]).then((v) => {console.log(v)},(e) => {console.log(e)}
)
Promise.race() 方法
如果race
的字面意思競賽
,該方法也是傳入一個Promise
對象的數組,不同點在于:先成功的Promise
將直接進入到then
的成功回調中。如果失敗了,也直接進入到失敗的then
回調。
function loadData() {return new Promise((resolve, reject) => {setTimeout(() => {resolve('請求成功')}, 3000)})
}
function timeOut() {return new Promise((resolve, reject) => {setTimeout(() => {reject('請求超時')}, 5000)})
}
Promise.race([loadData(), timeOut()]).then((v) => {console.log(v)},(e) => {console.log(e)}
)
async 和 await 關鍵字
這兩個關鍵字是Promise
方法的語法糖,底層的實現還是Promise
對象的那一套。優點在于能使異步編程的可讀性進一步加強,使其更接近于同步執行的語法。
- async 關鍵字
// async 語法糖的寫法
async function fn() {return '12345'
}
fn().then((v) => {console.log(v)
})
// 等同于下方的寫法
function fn() {return Promise.resolve('12345')}fn().then((v) => {console.log(v)})
- await 關鍵字
這個關鍵字必須在async
函數中使用。用于“等待” await
后的表達式執行,并接受該表達式的返回值。
// 函數 p() 返回的是一個 Promise 對象,
// 延時 1 秒后執行成功回調函數,相當于模擬一次異步請求
function p(msg) {return new Promise((resolve, reject) => {setTimeout(() => {// 將函數 p() 的實參值 msg 作為執行成功回調函數的返回值resolve(msg)}, 1000)})
}// 一個用于正常輸出內容的函數
function log() {console.log('2. 正在操作')
}async function fn() {console.log('1. 開始')await log()let p1 = await p('3. 異步請求')console.log(p1)console.log('4. 結束')
}
fn()
最后的執行順序參考下圖:

Proxy 代理
通過Proxy
代理可以為對象攔截一些特定的操作,proxy
對象對于原對象的操作最終會轉發給原對象,并且proxy
對于原對象的值都只是引用的。
創建
// 偽代碼
const proxy = new Proxy(target, handler)// 實際例子
const target = {}
const proxy = new Proxy(target, {})proxy.name = '悶墩兒'
console.log(proxy.name)
console.log(target.name)target.name = '憨憨'
console.log(proxy.name)
console.log(target.name)
其中最常用的攔截方法:
攔截方法 | 方法說明 |
---|---|
get(target, propKey, receiver) | 攔截對象屬性的讀取。 |
set(target, propKey, value, receiver) | 攔截對象屬性的設置。 |
has(target, propKey) | 攔截 propKey in proxy 的操作。 |
ownKeys(target) | 攔截 Object.getOwnPropertyNames(proxy) 、Object.getOwnPropertySymbols(proxy) 、Object.keys(proxy) 、for...in 循環,返回一個數組。 |
get 方法
通過在handler
對象中 加入get
方法來使用,該方法會在請求原對象(target)的某一鍵(propKey)的值時調用,并且原對象和鍵都會作為get
的回調參數。
const dog = { name: '悶墩兒' }
const proxy = new Proxy(dog, {get(target, propKey) {// 遍歷目標對象的屬性鍵值if (propKey in target) {return target[propKey] // 返回相應的屬性值} else {throw new ReferenceError(propKey + ' 屬性不存在')}},
})
console.log('訪問 dog 對象中的 name 屬性值為:' + proxy.name)
console.log('訪問不存在的 age 屬性:' + proxy.age)
set 方法
set
會在你想設置原對象(target)的某一鍵(propKey),并將該鍵對應的值設置成你傳入的值(value)時調用。額外需要知道的是返回值為設置成功與否的boolean
值。
const validator = {set(target, propKey, value) {if (propKey === 'age') {// 判斷 age 屬性值是否時數字if (!Number.isInteger(value)) {throw new TypeError('狗狗的年齡只能是整型哦!')}}target[propKey] = valuereturn true},
}const dog = new Proxy({}, validator)
dog.age = '22'
has 方法
該方法在使用in
查詢屬性時調用,該方法可以解決繼承時屬性繼承出現的問題:
場景一中:
valueOf
實際上是Object
的屬性,因為dog
默認繼承自Object
所以該屬性默認也是dog
的屬性。
// 場景一:解決的問題
const dog = { name: '悶墩兒' }
console.log('name' in dog)
console.log('valueOf' in dog)// 場景二:使用實例
const dog = { name: '悶墩兒', age: 2 }
const handler = {has(target, propKey) {if (propKey == 'age' && target[propKey] < 5) {console.log(`${target.name}的年齡小于 5 歲哦!`)return true}},
}
const proxy = new Proxy(dog, handler)console.log('age' in proxy)
ownKeys
在使用迭代方法例如for...in
迭代對象的鍵時可以使用ownKeys
攔截該迭代,并返回你想給的迭代數組。
注意,你給的數組中的元素如果不是原對象的屬性,將不會被迭代。
let dog = { name: '悶墩兒', age: 2, food: '狗罐頭' }
const proxy = new Proxy(dog, {ownKeys() {return ['name', 'color']},
})for (let key in proxy) {console.log(key) // 輸出 name
}