JavaScript 如何中止Promise

目录

方法 1:使用新的 Promise.withResolvers()

方法 2:使用 AbortController


在 JavaScript 中,你可能已经知道如何取消请求:对于 XHR 可以使用 xhr.abort() ,对于 fetch 可以使用 signal 。但是你如何取消一个普通的 Promise 呢?

目前,JavaScript 的 Promise 本身并不提供取消常规 Promise 的 API。因此,我们接下来要讨论的是如何丢弃 / 忽略 Promise 的结果。

方法 1:使用新的 Promise.withResolvers()

之前写法

let resolve, reject;

const promise = new Promise((res, rej) => {
   resolve = res;
   reject = rej;
 });let resolve, reject;
const promise = new Promise((res, rej) => {
   resolve = res;
   reject = rej;
 });

现在我们可以

 const { promise, resolve, reject } = Promise.withResolvers();

因此我们可以利用这一点来暴露一个名为 “cancel” 的方法:

const buildCancelableTask = <T>(asyncFn: () => Promise<T>) => {
   let rejected = false;
   const { promise, resolve, reject } = Promise.withResolvers<T>();

   return {
     run: () => {
       if (!rejected) {
         asyncFn().then(resolve, reject);
       }

       return promise;
     },

     cancel: () => {
       rejected = true;
       reject(new Error('CanceledError'));
     },
   };
 };

测试代码

 const sleep = (ms: number) => new Promise(res => setTimeout(res, ms));

 const ret = buildCancelableTask(async () => {
   await sleep(1000);
   return 'Hello';
 });

 (async () => {
   try {
     const val = await ret.run();
     console.log('val: ', val);
   } catch (err) {
     console.log('err: ', err);
   }
 })();

 setTimeout(() => {
   ret.cancel();
 }, 500);

这里,将任务预设为至少耗时1000毫秒,但我们在接下来的500毫秒内取消了任务,所以会看到:

注意,这并不是真正的取消,而是提前拒绝。原来的 asyncFn() 将继续执行,直到解析或拒绝为止,但这并不重要,因为用 Promise.withResolvers<T>() 创建的 Promise 已经被拒绝了。 

 

方法 2:使用 AbortController

就像我们取消获取请求一样,我们也可以实现一个监听器来实现提前拒绝。它看起来是这样的:

const buildCancelableTask = <T>(asyncFn: () => Promise<T>) => {
   const abortController = new AbortController();

   return {
     run: () =>
       new Promise<T>((resolve, reject) => {
         const cancelTask = () => reject(new Error('CanceledError'));

         if (abortController.signal.aborted) {
           cancelTask();
           return;
         }

         asyncFn().then(resolve, reject);

         abortController.signal.addEventListener('abort', cancelTask);
       }),

     cancel: () => {
       abortController.abort();
     },
   };
 };

它具有与上述相同的效果,但使用的是 AbortController。你可以在此处使用其他监听器,但 AbortController 提供了额外好处,即如果你多次调用 cancel ,它不会多次触发 'abort' 事件。

基于此代码,我们可以进一步构建可取消的获取操作。这在需要连续请求的场景下非常有用,例如您可能希望丢弃之前的请求结果并使用最新的请求结果。

 const buildCancelableFetch = <T>(
   requestFn: (signal: AbortSignal) => Promise<T>,
 ) => {
   const abortController = new AbortController();

   return {
     run: () =>
       new Promise<T>((resolve, reject) => {
         if (abortController.signal.aborted) {
           reject(new Error('CanceledError'));
           return;
         }

         requestFn(abortController.signal).then(resolve, reject);
       }),

     cancel: () => {
       abortController.abort();
     },
   };
 };
 
 const ret = buildCancelableFetch(async signal => {
   return fetch('http://localhost:5000', { signal }).then(res =>
     res.text(),
   );
 });
 
 (async () => {
   try {
     const val = await ret.run();
     console.log('val: ', val);
   } catch (err) {
     console.log('err: ', err);
   }
 })();
 
 setTimeout(() => {
   ret.cancel();
 }, 500);

请注意,这不会影响服务器处理逻辑;它只会导致浏览器拒绝 / 取消请求,换句话说,如果你发送 POST 请求以更新用户信息,它仍然可能生效。因此,这种方法更常用于在获取新数据时发送 GET 请求的场景。

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScriptPromise是一种用于处理异步操作的机制。它可以让我们更优雅地处理回调函数嵌套的问题,使得异步操作的代码更易于阅读和维护。 一个Promise对象代表一个异步操作的最终完成或失败,并且它的状态只能从“未完成”转变为“完成”或“失败”。在完成或失败时,Promise对象会传递一个结果或原因。 使用Promise对象的步骤如下: 1. 创建一个Promise对象,传入一个执行器函数(executor function),该函数接收两个参数:resolve和reject。 2. 在执行器函数中,执行异步操作,并在异步操作完成或失败时调用resolve或reject函数。 3. 调用Promise对象的then方法,传入两个回调函数:一个用于处理成功结果,一个用于处理失败原因。 4. 在then方法中,处理成功结果或失败原因。 例如,下面是一个使用Promise对象的例子: ```javascript function fetchData() { return new Promise((resolve, reject) => { setTimeout(() => { const data = { name: 'John Doe', age: 30 }; resolve(data); }, 2000); }); } fetchData() .then(data => { console.log(data.name); // 输出 John Doe }) .catch(error => { console.error(error); }); ``` 在上面的例子中,我们使用了Promise对象来模拟一个异步操作,该操作将在2秒后返回一个包含姓名和年龄的对象。然后,我们通过调用then方法传入一个处理成功结果的回调函数,打印出该对象的姓名属性。如果异步操作失败,我们通过调用catch方法传入一个处理失败原因的回调函数,输出错误信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值