Ajax cancel

如果你相熟 xhr,会晓得 Ajax 其实能够前端被动勾销,应用的是 XMLHttpRequest.abort()。当然当初也不是刀耕火种的时代,除了面试,可能根本不会手写 xhr,在无人不知的 axios 中有两种勾销办法:

首先是老式 cancelToken:

const CancelToken = axios.CancelTokenconst 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    }  })axios.post(  '/user/12345',  {    name: 'new name',  },  {    cancelToken: source.token,  })// cancel the request (the message parameter is optional)source.cancel('Operation canceled by the user.')

而后是新玩意(其实也不新)AbortController:

const controller = new AbortController()axios  .get('/foo/bar', {    signal: controller.signal,  })  .then(function (response) {    //...  })// cancel the requestcontroller.abort()

cancelToken 和 signal 传到 axios 之后,都会以某种机制调用 XMLHttpRequest.abort()

onCanceled = (cancel) => {  if (!request) {    return  }  reject(    !cancel || cancel.type ? new CanceledError(null, config, request) : cancel  )  request.abort()  request = null}config.cancelToken && config.cancelToken.subscribe(onCanceled)if (config.signal) {  config.signal.aborted    ? onCanceled()    : config.signal.addEventListener('abort', onCanceled)}

cancelToken 是利用公布订阅模式告诉 axios 勾销申请,尽管这部分是 axios 本人实现的,然而源自于一个 tc39 提案 cancelable promises proposal,不过这个提案被废除了。

而 AbortController 是曾经能够在浏览器应用的接口,顾名思义,这就是一个专门用于停止行为的控制器。mdn 的举例用的也是 Ajax 申请,不过是至潮至 in 的 fetch,从中可见 axios 跟 fetch 的实际是统一的:

function fetchVideo() {  controller = new AbortController() // 新建一个 controller  const signal = controller.signal  fetch(url, { signal }) // 在 fetch 办法传入 signal    .then(function (response) {      console.log('Download complete', response)    })    .catch(function (e) {      console.log('Download error: ' + e.message)    })}abortBtn.addEventListener('click', function () {  if (controller) controller.abort() // 调用 controller.abort 勾销 fetch  console.log('Download aborted')})

AbortController 的其余用处

当然 AbortController 不只有停止 Ajax 一个性能,通过查看 dom 标准文档还能看到两个应用示例:

一个比拟实用的例子是用 AbortController 勾销事件监听:

dictionary AddEventListenerOptions : EventListenerOptions {  boolean passive = false;  boolean once = false;  AbortSignal signal;};

通过向 AddEventListener 传入 signal,运行 abort() 即可勾销事件监听,这个办法对匿名回调函数尤其有用。

另一个例子是用于停止 promise。这是一个比拟简洁且自文档的办法……不过其实实现这个性能也不是非要 AbortController 能力做到,只有想方法拿到 promise 的 reject 就好了。我感觉这个例子的重点偏差于学会应用 signal 的 onabort:

const controller = new AbortController();const signal = controller.signal;startSpinner();doAmazingness({ ..., signal })  .then(result => ...)  .catch(err => {    if (err.name == 'AbortError') return;    showUserErrorMessage();  })  .then(() => stopSpinner());// …controller.abort();function doAmazingness({signal}) {  return new Promise((resolve, reject) => {    signal.throwIfAborted();    // Begin doing amazingness, and call resolve(result) when done.    // But also, watch for signals:    signal.addEventListener('abort', () => {      // Stop doing amazingness, and:      reject(signal.reason);    });  });}

总之,signal 就是个繁难发信器,而且性能偏差于勾销某操作。如果在某种状况下不想本人实现一个 pubsub 对象的话,用这个就完事了。

AbortController 的介绍就到此为止吧,不晓得大家有没有逐步遗记题目……最初是想讨论一下,勾销 Ajax 到底有没有用?

勾销还是不勾销,这是个问题

事实上,这个 Ajax 勾销只是前端自说自话,后端并不知道要停止,发过来的申请还是要执行的,后端没有非凡解决的话 10s 的申请你勾销了后端也依然在吃力地跑。

那么在一些文章中看到的“优化”,所谓“勾销申请,只保留最初一个”是否真的有意义呢?

分状况探讨,对于 POST 等批改数据的申请,每次发送即便返回慢,服务器也曾经在解决了,勾销上一个 POST 再反复发一个无疑是弱智行为。

对于 GET,且仅针对某些极限操作,或者有一点成果,例如:获取一个超长 table,后果没拿到,而后用户就用搜寻疾速返回大量数据并且渲染了,等到超长 table 真正返回就会笼罩掉搜寻的数据,这个状况 cancel 是真的无效的。另外还有下载上传的勾销,不过预计也很少会用到。

最初再说一个有情理然而事实上也是没什么用的益处:cancel 之后能省一个申请地位,毕竟浏览器一个域名的同时申请数量是有限度的,更多状况下,比 cancel 更常见的 timeout 更实用。嗯……除非同时排着五六个超慢申请,否则轮转还是比拟快的……

集体倡议是,说到底这个所谓“勾销”都是极非凡状况的非凡解决,晓得这回事就好了,没有必要没事就在拦截器里整个勾销操作。

参考

  • dom spec
  • GitHub axios
  • mdn AbortController