使用 Promise.withResolvers 实现合并连续请求
使用 Promise.withResolvers()
合并连续请求。
方案一:返回最早的未落定期约
策略:存在未落定的请求时,后续请求返回该期约。该策略思路简明,实现也非常简单。
js
/**
* 合并相同请求(连续调用包装后的请求方法,前一次调用未完成时,后一次调用返回前一次调用的结果)
*
* @param {Function} request
* @returns {Object} { mergedAsyncRequest: 合并后的异步请求方法 }
* @example
* import useMergeContinuousAsync from '@/use/merge-continuous-async'
*
* const originalRequestFunction = (...args) => axios.get(...args)
* const { mergedAsyncRequest } = useMergeContinuousAsync(originalRequestFunction)
* mergedAsyncRequest(111)
* setTimeout(mergedAsyncRequest, 200, 222)
*/
export default request => {
if (!(request instanceof Function)) throw new Error('request must be a function')
let promise, resolve, reject
function mergedAsyncRequest(...args) {
if (promise) return promise
;({ promise, resolve, reject } = Promise.withResolvers())
request(...args)
.then(resolve, reject)
.finally(() => {
promise = undefined
})
return promise
}
return { mergedAsyncRequest }
}
vue
<script setup>
import { ref } from 'vue'
import useMergeContinuousAsync from './merge-continuous-async'
const reqs = ref([])
const { mergedAsyncRequest } = useMergeContinuousAsync(testApi)
async function addAsyncReq() {
const timestamp = +new Date()
reqs.value.push({
timestamp,
loaded: false,
resFlag: '',
})
let currReq
try {
const res = await mergedAsyncRequest(timestamp)
currReq = reqs.value.find(r => r.timestamp === timestamp)
currReq.resFlag = res.data[0]
} catch (err) {}
if (currReq) currReq.loaded = true
}
function testApi(...args) {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000, { data: args })
})
}
</script>
<template>
<h1>Merge continuous async</h1>
<button @click="addAsyncReq">Add async request</button>
<hr />
<label>Async request list:</label>
<div v-for="item in reqs" :key="item.timestamp">
<label>Flag: {{ item.timestamp }}, </label>
<span>{{ item.loaded ? 'loaded' : 'loading' }}; </span>
<span>resFlag:{{ item.resFlag }}</span>
</div>
</template>
方案二:取消旧的请求
参考连续接口请求,处理方案可以是取消所有旧的请求,仅保留最新的请求。
js
export default request => {
if (!(request instanceof Function)) throw new Error('request must be a function')
let promise, resolve, reject
function mergedAsyncRequest(...args) {
if (promise) {
reject(new Error('Cancalled'))
}
;({ promise, resolve, reject } = Promise.withResolvers())
request(...args).then(resolve, reject)
return promise
}
return { mergedAsyncRequest }
}
第 7 行存在一个无效取消的问题,但拒绝一个已落定的期约不会生效也不会报错
方案三:返回最后的未落定期约
如果希望实现的是合并,但以最新请求的落定为准,情况可能会有点复杂,需要将未落定的期约保持与最新的期约一致。
Last updated: