JavaScript 中断请求的几种方法

中断Promise

中断Promise不等同于中止Promise,因为Promise是无法被终止的.

这里的中断指的是,在合适的时机,把pending状态的promise给reject掉。例如一个常见的应用场景就是给网络请求设置超时时间,一旦超时就中断。

还是用 setTimeout 来模拟网络请求。阀值设置为Math.random() * 3000表示随机3秒之内返回结果。

const request = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('收到服务端数据')
  }, Math.random() * 3000)
});

假设超过2秒就是网络超时,我们可以封装一个超时处理函数。
由于网络请求所需的事件是随机的,因此可以利用Promise.race(race方法返回第一个异步任务完成时,并不关心它的最终状态 即它可以成功也可以失败)方法,达到超时reject的目的。

const timeoutReject = (p1, timeout = 2000) => {
    const p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('网络超时');
        }, timeout);
    });
    return Promise.race([p1, p2]);
};
 
timeoutReject(request).then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});

包装cancel方法——仿照Axios的CancelToken

上面实现的方式并不灵活,因为中断Promise的方式有很多,不单单是网络超时.
我们可以仿照Axios中CancelToken的核心源码,简单包装一个cancel方法,供使用者随时调用.

function cancelWrapper(p1) {
    let cancel;
    const p2 = new Promise((resolve, reject) => {
        cancel = reject;
    });
    // 如果没有resolve或reject,p2的状态永远是pending
    const p = Promise.race([p1, p2]);
    p.cancel = cancel;
    return p;
}
 
const req = cancelWrapper(request);
req.then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});
 
setTimeout(() => {
    // 手动调用req.cancel,将p2的状态改变为rejected
    req.cancel('手动中断请求');
}, 2000);

如此封装的主要目的就是为了能够在Promise外部控制其resolve或reject,让使用者可以随时手动调用resolve(触发.then)或reject(触发.catch)。

又或者我们换个写法:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Document</title>
</head>
<body>
<button id="send">Send</button>
<button id="cancel">Cancel</button>

<script>
  class CancelToken {
    constructor(cancelFn) {
      this.promise = new Promise((resolve, reject) => {
        cancelFn(() => {
          resolve("delay cancelled");
        });
      });
    }
  }

  const sendButton = document.querySelector("#send");
  const cancelButton = document.querySelector("#cancel");

  function cancellableDelayedResolve(delay) {
    console.log("prepare send request");
    return new Promise((resolve, reject) => {
      const id = setTimeout(() => {
        console.log("ajax get data");
      }, delay);
      const cancelToken = new CancelToken((cancelCallback) => {
        cancelButton.addEventListener("click", cancelCallback);
      });
      cancelToken.promise.then((res) => {
        console.log('cancelToken', res);
        clearTimeout(id);
      });
    });
  }

  sendButton.addEventListener("click", () => {
    cancellableDelayedResolve(2000).then(res => {
      console.log(res);
    });
  });
</script>
</body>
</html>

在上面的案例中,我们是先预置一个 promise,也就是 CancelToken,它的作用就是在需要的时候能够随时调用 并在其中加关闭逻辑。

上面我们提到了Axios 的 CancelToken,接下来我们来看下:
方法1:

import axios from 'axios';
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 {
    // handle error
  } 
});
 
source.cancel('Operation canceled by the user.');

方法2:

import axios from 'axios';
const CancelToken = axios.CancelToken;
 
// 创建一个变量如 cancel 用于存储这个中断某个请求的方法
let cancel;
 
axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c; // 将参数 c 赋值给 cancel
  })
});
 
// 判断 cancel 是否为函数,确保 axios 已实例化一个CancelToken
if (typeof cancel === 'function') {
    cancel();
    cancel = null;
}
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值