慢请求处理

146 阅读1分钟

场景

有一个接口是向后端请求数据的,地址/slow,单次耗时比较久,当用户点击请求就会发送请求。

问题

当用户频繁触发或者误触发这个请求的时候,就会导致问题:

  1. 第二次请求可能会被第一次覆盖(因为不确定返回时间
  2. 增加不必要的等待时间(用户可能就不想要之前的请求

想法

如果请求可以取消,那么问题就解决了

理论

支持使用 CancelToken 取消请求,存在一下特性:

  1. 一个 CancelToken 可以绑定多个请求
  2. 当触发 cancel 方法之后,过程不可逆,相关绑定的请求都无法使用
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
    cancelToken: source.token
}).catch(function (thrown) {
    if (axios.isCancel(thrown)) {
        console.log('Request canceled', thrown.message);
    } else {
        // 处理错误
    }
});

axios.post('/user/12345', {
    name: 'new name'
}, {
    cancelToken: source.token
})

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

融入开发

做了一些必要的封装 Make life easier

// 慢请求处理
export class SlowRequest {
    constructor() {
        this.cancelToken = axios.CancelToken.source()
    }

    async request() {
        this.cancelToken.cancel()
        this.cancelToken = axios.CancelToken.source()
        return await axios({
            url: 'http://localhost:3000/slow',
            cancelToken: this.cancelToken.token
        })
    }

    cancel() {
        this.cancelToken.cancel()
    }
}

// Demo 组件
export const TestCancel = (props) => {

    const [loading, setLoading] = useState(0)
    const [client, _] = useState(new SlowRequest())

    const onQuery = async () => {
        setLoading(old => old + 1)

        try{
            const res = await client.request()
        }catch(e) {
            console.log(e)
        }

        setLoading(old => old - 1)
    }

    const onCancel = () => {
        client.cancel()
    }


    return (
        <div>
            {
                loading > 0 ? 'loading' : 'content'
            }
            <button onClick={onQuery}>query</button>
            <button onClick={onCancel}>cancel</button>
        </div>
    )
}

不足

  1. 由于只是记录想法,所以封装其实并不到位,像 URL、Client 都可以再次抽离