JavaScript中实现中断异步函数的几种方法

前端 潘老师 4周前 (03-28) 26 ℃ (0) 扫码查看

JavaScript编程中,我们常常会遇到需要中断正在执行的异步函数的情况。比如在发起网络请求时,用户突然取消操作,或者某个异步任务执行时间过长,我们希望能够及时终止它。接下来,就为大家详细介绍几种常见且有效的中断异步函数的方法。

一、借助AbortController中断fetch请求

AbortController是浏览器和Node.js都内置的一个API,它主要用来取消异步操作,像fetch发起的网络请求就可以用它来中断。下面这段代码展示了具体的实现方式:

// 创建一个AbortController实例,用于控制异步操作的终止
const controller = new AbortController();
// 获取信号对象,用于传递给异步操作,以便在需要时中断它
const signal = controller.signal;

// 发起一个fetch请求,并将信号对象传递进去
fetch('https://example.com/api/data', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => {
    // 判断错误类型,如果是因为操作被取消导致的错误
    if (error.name === 'AbortError') {
      console.log('请求已被取消');
    } else {
      // 其他类型的错误,打印错误信息
      console.error('请求出错:', error);
    }
  });

// 在某个时刻,比如用户点击了取消按钮等情况下,调用abort方法取消请求
controller.abort();

在这段代码里,我们先创建了AbortController实例及其信号对象,然后把信号对象传入fetch请求。当调用controller.abort()时,fetch请求就会被中断,并在catch块中捕获到AbortError错误。

二、在自定义异步函数中运用AbortController

除了中断fetch请求,AbortController在自定义异步函数里也能发挥作用,实现中断功能。示例代码如下:

// 定义一个自定义异步函数,接收一个信号对象作为参数
function customAsyncFunction(signal) {
  return new Promise((resolve, reject) => {
    // 使用setInterval模拟异步操作,每1秒执行一次
    const intervalId = setInterval(() => {
      console.log('异步操作正在执行...');
      // 检查信号对象的aborted属性,如果为true,表示操作被取消
      if (signal.aborted) {
        // 清除定时器,停止异步操作
        clearInterval(intervalId);
        // 抛出一个带有错误信息的DOMException,标记操作已被取消
        reject(new DOMException('操作已被取消', 'AbortError'));
      }
    }, 1000);
  });
}

// 创建AbortController实例和信号对象
const controller = new AbortController();
const signal = controller.signal;

// 调用自定义异步函数,并传入信号对象
customAsyncFunction(signal)
  .then(() => console.log('异步操作完成'))
  .catch(error => {
    // 处理错误,判断是否是因为操作被取消导致的错误
    if (error.name === 'AbortError') {
      console.log('异步操作已被取消');
    } else {
      // 其他错误,打印错误信息
      console.error('异步操作出错:', error);
    }
  });

// 延迟3秒后,调用abort方法取消异步操作
setTimeout(() => {
  controller.abort();
}, 3000);

在这个示例中,customAsyncFunction函数内部会根据传入的信号对象状态来决定是否停止异步操作。通过setTimeout延迟3秒后调用controller.abort(),就能中断这个自定义的异步函数。

(一)使用变量控制异步函数中断

除了AbortController,我们还可以用一个变量来控制自定义异步函数的中断,这种方式比较基础和直观。代码示例如下:

// 定义一个自定义异步函数
function customAsyncFunction() {
    // 定义一个变量,用于标记异步操作是否被取消
    let isAborted = false;

    return {
        // 返回一个Promise对象,在Promise内部模拟异步操作
        promise: new Promise((resolve, reject) => {
            // 使用setInterval模拟异步操作,每1秒执行一次
            const intervalId = setInterval(() => {
                console.log('异步操作正在执行...');
                // 如果isAborted为true,说明操作被取消
                if (isAborted) {
                    // 清除定时器,停止异步操作
                    clearInterval(intervalId);
                    // 抛出错误,标记操作已被取消
                    reject(new Error('操作已被取消'));
                }
            }, 1000);
        }),
        // 定义一个abort方法,用于设置isAborted为true,取消异步操作
        abort: () => {
            isAborted = true;
        }
    };
}

// 调用自定义异步函数,获取包含Promise和abort方法的对象
const asyncTask = customAsyncFunction();

// 处理异步操作的结果或错误
asyncTask.promise
   .then(() => console.log('异步操作完成'))
   .catch(error => {
        console.error('异步操作出错:', error.message);
    });

// 延迟3秒后,调用abort方法取消异步操作
setTimeout(() => {
    asyncTask.abort();
}, 3000);

在这段代码里,customAsyncFunction返回一个包含Promiseabort方法的对象。abort方法可以改变isAborted变量的值,从而中断异步操作。

(二)使用变量控制和AbortController对比

  1. 使用变量控制的优缺点
    • 优点:这种方式简单直接,不需要学习新的API,对于小型项目或者简单的异步控制场景来说,很容易理解和实现。
    • 缺点:缺乏标准化,在不同的异步函数中都要重复编写类似的控制逻辑,这使得代码的可维护性和扩展性较差。当项目规模逐渐变大,管理多个异步操作的中断状态会变得很复杂。
  2. 使用AbortController的优缺点
    • 优点AbortController是JavaScript官方标准化的API,有统一的使用方式和错误处理机制。它可以在不同的异步操作(如fetch请求、自定义异步函数等)中复用,大大增强了代码的可维护性和一致性。在与第三方库或框架集成时,和其他使用AbortController的代码交互也更方便。
    • 缺点:对于初学者而言,需要额外学习AbortController的使用方法,存在一定的学习成本。

总体来说,使用变量控制能满足基本的异步函数中断需求,但在复杂项目中,AbortController是更优雅、更值得推荐的选择。

三、利用Promise.race实现超时中断

Promise.race也可以用来实现异步操作的超时中断。下面通过代码示例来看看具体怎么做:

// 定义一个异步操作函数,模拟一个需要5秒才能完成的任务
function asyncOperation() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('操作完成');
    }, 5000);
  });
}

// 定义一个超时函数,在指定时间后抛出错误
function timeout(ms) {
  return new Promise((_, reject) => {
    setTimeout(() => {
      reject(new Error('操作超时'));
    }, ms);
  });
}

// 使用Promise.race,同时执行异步操作和超时判断
Promise.race([asyncOperation(), timeout(3000)])
  .then(result => console.log(result))
  .catch(error => console.error(error.message));

在这段代码中,Promise.race会同时执行asyncOperationtimeout这两个Promise。如果timeout先完成(即3秒内asyncOperation没有完成),就会触发catch块,提示操作超时;如果asyncOperation在3秒内完成,就会执行then块,打印操作完成的结果。

通过上述这些方法,我们可以在不同的场景下,优雅地中断正在执行的JavaScript异步函数,提升程序的灵活性和用户体验。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/front/16422.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】