ES6异步编程中Promise与Proxy对象

Promise 对象

Promise对象用于解决Javascript中的地狱回调问题,有效的减少了程序回调的嵌套调用。

创建

如果要创建一个Promise对象,最简单的方法就是直接new一个。但是,如果深入学习,会发现使用Promise下的静态方法Promise.resolve()也能创建一个Promise对象:

js 复制代码
// 创建方法一
new Promise((resolve, reject) => {
  // 此处做一个异步的事情
});

// 创建方法二
Promise.resolve(p)	// p 可以是一个Promise,也可以是一个普通的数值。

使用方法二创建Promise时,可以传入一个普通的值,或一个Promise对象。最后都会作为一个Promise返回出来。如果传入的是一个普通的值,产生的Promise的值就会将这个值传入resolve方法发送给下一个then

使用

对于Promise对象的使用,参考下方的案例,对于Promise的使用,理解返回值、参数、两个回调之间的关系后会有一定的帮助。

第二种写法的区别主要在于直接在第一次定义Promise的同时把下一次then中的回调也顺便地写好了。

js 复制代码
// 案例一
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的失败回调中。

js 复制代码
// 语法演示的伪代码
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回调。

js 复制代码
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 关键字
js 复制代码
// 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后的表达式执行,并接受该表达式的返回值。

js 复制代码
// 函数 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对于原对象的值都只是引用的。

创建

js 复制代码
// 伪代码
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的回调参数。

js 复制代码
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值。

js 复制代码
const validator = {
  set(target, propKey, value) {
    if (propKey === 'age') {
      // 判断 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的属性。

js 复制代码
// 场景一:解决的问题
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拦截该迭代,并返回你想给的迭代数组。

注意,你给的数组中的元素如果不是原对象的属性,将不会被迭代。

js 复制代码
let dog = { name: '闷墩儿', age: 2, food: '狗罐头' }
const proxy = new Proxy(dog, {
  ownKeys() {
    return ['name', 'color']
  },
})

for (let key in proxy) {
  console.log(key) // 输出 name
}
相关推荐
陈奕昆19 分钟前
【LLaMA-Factory实战】Web UI快速上手:可视化大模型微调全流程
前端·ui·llama·大模型微调实战
Jedi Hongbin1 小时前
echarts自定义图表--柱状图-横向
前端·javascript·echarts
3D虚拟工厂2 小时前
1️⃣7️⃣three.js_OrbitControls相机控制器
javascript·3d·vue·blender·three.js·uv
Yan-英杰2 小时前
npm error code CERT_HAS_EXPIRED
服务器·前端·数据库·人工智能·mysql·npm·node.js
sunly_2 小时前
Flutter:组件10、倒计时
开发语言·javascript·flutter
徐白11773 小时前
Node.js 事件循环和线程池任务完整指南
开发语言·javascript·node.js
哈希茶馆3 小时前
前端工程化利器:Node.js 文件匹配库 fast-glob 完全指南——比传统方案快 350% 的「文件搜索神器」
运维·前端·javascript·npm·node.js·全文检索·运维开发
软件开发技术深度爱好者3 小时前
HTML5+JavaScript实现连连看游戏之二
javascript·游戏·html5
zhangguo20023 小时前
react18基础速成
前端·javascript·react.js
海盐泡泡龟4 小时前
Vue中的过滤器知道多少?从是什么、怎么用、应用场景、原理分析、示例解释
前端·vue.js·flutter