JavaScript实现锁功能,同一时间多次异步请求函数都取到返回值,只触发一次异步请求

问题

有个需求,某个异步请求函数可能被多次调用,重复调用消耗资源,需要对其进行优化

  1. 每次调用该函数都能取到返回值
  2. 只发送一次异步请求

这个和节流、防抖功能不一样,节流防抖会丢弃掉中间的请求,中间的请求获取不到返回值,这里要求每一个函数调用都能取到返回值。

方案一

很容易想到使用同步非阻塞方案,在第一个点击时,进入loading状态,之后的点击判断loading就等50毫秒继续检查loading的值,直到loading为false,返回localEnv。这里使用setTimeout来模拟异步请求。

<body>
  <input onclick="clickMe()" type="button" value="点我">
</body>
async function clickMe() {
  const env = await getEnv()
  console.log(env)
}

let localEnv
let loading = false
async function getEnv() {
  if (localEnv) {
    return localEnv
  }
  // 若正在请求中,则每50毫秒检查loading状态,直到为false,返回请求结果
  if (loading) {
    // 同步非阻塞
    while(loading) {
      await wait(50)
    }
    return localEnv
  } else {
    loading = true
    return new Promise((resolve, reject) => {
      console.log('questing env......')
      setTimeout(() => {
        localEnv = {
          platform: '141001',
          appid: 'aoshdakhpa8fkdng'
        }
        loading = false
        resolve(localEnv)
      }, 2000)
    })
  }
}
function wait (time) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, time)
  })
}

在这里插入图片描述

方案一主要由于循环去检测loading的状态,导致不那么高效

方案二

Java中可以通过锁机制,使用wait/notify轻易实现该功能。JavaScript也可以利用锁机制来实现类似wait/notify的功能。
JavaScript可以通过resolve/reject函数来实现锁,在没有调用resolve/reject函数时,promise会看起来相当于一直阻塞(其实和Java的阻塞不一样,这里只是没有执行后续的函数)。

<body>
  <input onclick="clickMe()" type="button" value="点我">
</body>
async function clickMe() {
  const env = await getEnv()
  console.log(env)
}

let localEnv, p
const queue = []
let loading = false

async function getEnv() {
  return new Promise((resolve, reject) => {
    if (localEnv) {
      resolve(localEnv)
    }
    // 进入这个if的Promise没有调用resolve(),会一直阻塞
    if (loading) {
      // 把resolve存入数组,待请求返回后执行resolve
      queue.push({resolve, reject})
    }
    if (!loading && !localEnv) {
      loading = true
      console.log('questing env......')
      setTimeout(() => {
        localEnv = {
          platform: '141001',
          appid: 'aoshdakhpa8fkdng'
        }
        loading = false
        resolve(localEnv)
        // 异步请求结束,调用所有正在阻塞的Promise的resolve函数,返回结果,解除阻塞
        while (p = queue.shift()) {
          p.resolve(localEnv)
        }
      }, 2000)
    }
  })
}

在这里插入图片描述

结语

利用好Promise没有resolve/reject会一直阻塞的特性,可以实现类似Java的wait/notify功能,实现同一时间多次异步请求函数都取到返回值,只触发一次异步请求的功能。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Vue3 中,可以使用 axios 或者 fetch 进行异步请求。下面是一个使用 axios 进行异步请求的示例: ```javascript import axios from 'axios'; import { ref } from 'vue'; export default { setup() { const data = ref([]); axios.get('/api/data').then(response => { data.value = response.data; }).catch(error => { console.log(error); }); return { data, }; }, }; ``` 在这个示例中,我们首先导入了 axios 和 ref 函数。然后,我们在 setup 函数中定义了一个名为 data 的 ref 值,它用来存储从服务器获取的数据。 接下来,我们使用 axios.get 方法向服务器发起 GET 请求,并使用 then 和 catch 方法处理响应和错误。在成功获取数据后,我们将其赋值给 data.value。 最后,我们将 data 的值作为 setup 函数返回值,这样就可以在模板中使用它了。 需要注意的是,由于 setup 函数是在组件实例化之前执行的,因此我们无法使用 this 关键字来访问组件的属性和方法。如果需要访问组件的属性和方法,可以使用 setup 函数的第二个参数来传递组件实例。例如: ```javascript import axios from 'axios'; import { ref } from 'vue'; export default { props: ['id'], setup(props, { emit }) { const data = ref([]); axios.get(`/api/data/${props.id}`).then(response => { data.value = response.data; }).catch(error => { console.log(error); }); return { data, emit, }; }, }; ``` 在这个示例中,我们定义了一个名为 id 的 props 属性,并在 setup 函数的第一个参数中访问它。我们还使用 setup 函数的第二个参数来访问 emit 方法,以便在组件中触发事件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值