Promise 对象
Promise
对象用于解决Javascript
中的地狱回调问题,有效的减少了程序回调的嵌套调用。
创建
如果要创建一个Promise
对象,最简单的方法就是直接new
一个。但是,如果深入学习,会发现使用Promise
下的静态方法Promise.resolve()
也能创建一个Promise
对象:
1 2 3 4 5 6 7
| new Promise((resolve, reject) => { });
Promise.resolve(p)
|
使用方法二创建Promise
时,可以传入一个普通的值,或一个Promise
对象。最后都会作为一个Promise
返回出来。如果传入的是一个普通的值,产生的Promise
的值就会将这个值传入resolve
方法发送给下一个then
。
使用
对于Promise
对象的使用,参考下方的案例,对于Promise
的使用,理解返回值、参数、两个回调之间的关系后会有一定的帮助。
第二种写法的区别主要在于直接在第一次定义Promise
的同时把下一次then
中的回调也顺便地写好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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
的失败回调中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 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
回调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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
对象的那一套。优点在于能使异步编程的可读性进一步加强,使其更接近于同步执行的语法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| async function fn() { return '12345' } fn().then((v) => { console.log(v) })
function fn() { return Promise.resolve('12345') } fn().then((v) => { console.log(v) })
|
这个关键字必须在async
函数中使用。用于“等待” await
后的表达式执行,并接受该表达式的返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
function p(msg) { return new Promise((resolve, reject) => { setTimeout(() => { 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
对于原对象的值都只是引用的。
创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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
的回调参数。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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
值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const validator = { set(target, propKey, value) { if (propKey === 'age') { if (!Number.isInteger(value)) { throw new TypeError('狗狗的年龄只能是整型哦!') } } target[propKey] = value return true }, }
const dog = new Proxy({}, validator) dog.age = '22'
|
has 方法
该方法在使用in
查询属性时调用,该方法可以解决继承时属性继承出现的问题:
场景一中:valueOf
实际上是Object
的属性,因为dog
默认继承自Object
所以该属性默认也是dog
的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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
拦截该迭代,并返回你想给的迭代数组。
注意,你给的数组中的元素如果不是原对象的属性,将不会被迭代。
1 2 3 4 5 6 7 8 9 10
| let dog = { name: '闷墩儿', age: 2, food: '狗罐头' } const proxy = new Proxy(dog, { ownKeys() { return ['name', 'color'] }, })
for (let key in proxy) { console.log(key) }
|