JavaScript前端超时异步操作完美解决过程

 更新时间:2021年11月25日 16:15   作者:厦门在乎科技
这篇文章主要为大家介绍了JavaScript前端超时异步操作的完美解决方式,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步

自从 ECMAScript 的 Promise ES2015 和 async/await ES2017 特性发布以后,异步在前端界已经成为特别常见的操作。异步代码和同步代码在处理问题顺序上会存在一些差别,编写异步代码需要拥有跟编写同步代码不同的“意识”。

如果一段代码久久不能执行完成,会怎么样?

如果这是同步代码,我们会看到一种叫做“无响应”的现象,或者通俗地说 —— “死掉了”;但是如果是一段异步代码呢?可能我们等不到结果,但别的代码仍在继续,就好像这件事情没有发生一般。

当然事情并不是真的没发生,只不过在不同的情况下会产生不同的现象。比如有加载动画的页面,看起来就是一直在加载;又比如应该进行数据更新的页面,看不到数据变化;

再比如一个对话框,怎么也关不掉 …… 这些现象我们统称为 BUG。但也有一些时候,某个异步操作过程并没有“回显”,它就默默地死在那里,没有人知道,待页面刷新之后,就连一点遗迹都不会留下。

Axios 自带超时处理

使用 Axios 进行 Web Api 调用就是一种常见的异步操作过程。通常我们的代码会这样写:

try {
    const res = await axios.get(url, options);
    // TODO 正常进行后续业务
} catch(err) {
    // TODO 进行容错处理,或者报错
}

这段代码一般情况下都执行良好,直到有一天用户抱怨说:怎么等了半天没反应?

然后开发者意识到,由于服务器压力增大,这个请求已经很难瞬时响应了。考虑到用户的感受,加了一个 loading 动画:

try {
    showLoading();
    const res = await axios.get(url, options);
    // TODO 正常业务
} catch (err) {
    // TODO 容错处理
} finally {
    hideLoading();
}

然而有一天,有用户说:“我等了半个小时,居然一直在那转圈圈!”于是开发者意识到,由于某种原因,请求被卡死了,这种情况下应该重发请求,或者直接报告给用户 —— 嗯,得加个超时检查。

幸运的是 Axios 确实可以处理超时,只需要在 options 里添加一个 timeout: 3000 就能解决问题。如果超时,可以在 catch 块中检测并处理:

try {...}
catch (err) {
    if (err.isAxiosError && !err.response && err.request
        && err.message.startsWith("timeout")) {
        // 如果是 Axios 的 request 错误,并且消息是延时消息
        // TODO 处理超时
    }
}
finally {...}

Axios 没问题了,如果用 fetch() 呢?

处理 fetch() 超时

fetch() 自己不具备处理超时的能力,需要我们判断超时后通过 AbortController 来触发“取消”请求操作。

如果需要中断一个 fetch() 操作,只需从一个 AbortController 对象获取 signal,并将这个信号对象作为 fetch() 的选项传入。大概就是这样:

const ac = new AbortController();
const { signal } = ac;
fetch(url, { signal }).then(res => {
    // TODO 处理业务
});
 
// 1 秒后取消 fetch 操作
setTimeout(() => ac.abort(), 1000);

ac.abort() 会向 signal 发送信号,触发它的 abort 事件,并将其 .aborted 属性置为 truefetch() 内部处理会利用这些信息中止掉请求。

上面这个示例演示了如何实现 fetch() 操作的超时处理。如果使用 await 的形式来处理,需要把 setTimeout(...) 放在 fetch(...) 之前:

const ac = new AbortController();
const { signal } = ac;
setTimeout(() => ac.abort(), 1000);
const res = await fetch(url, { signal }).catch(() => undefined); 

为了避免使用 try ... catch ... 来处理请求失败,这里在 fetch() 后加了一个 .catch(...) 在忽略错误的情况。如果发生错误,res 会被赋值为 undefined。实际的业务处理可能需要更合理的 catch() 处理来让 res 包含可识别的错误信息。

本来到这里就可以结束了,但是对每一个 fetch() 调用都写这么长一段代码,会显得很繁琐,不如封装一下:

async function fetchWithTimeout(timeout, resoure, init = {}) {
    const ac = new AbortController();
    const signal = ac.signal;
    setTimeout(() => ac.abort(), timeout);
    return fetch(resoure, { ...init, signal });
}

没问题了吗?不,有问题。

如果我们在上述代码的 setTimeout(...) 里输出一条信息:

setTimeout(() => {
    console.log("It's timeout");
    ac.abort();
}, timeout);

并且在调用的给一个足够的时间:

fetchWithTimeout(5000, url).then(res => console.log("success"));

我们会看到输出 success,并在 5 秒后看到输出 It's timeout

对了,我们虽然为 fetch(...) 处理了超时,但是并没有在 fetch(...) 成功的情况下干掉 timer。作为一个思维缜密的程序员,怎么能够犯这样的错误呢?干掉他!

async function fetchWithTimeout(timeout, resoure, init = {}) {
    const ac = new AbortController();
    const signal = ac.signal;    
    const timer = setTimeout(() => {
        console.log("It's timeout");
        return ac.abort();
    }, timeout);    
    try {
        return await fetch(resoure, { ...init, signal });
    } finally {
        clearTimeout(timer);
    }
}

完美!但问题还没结束。

万物皆可超时

Axios 和 fetch 都提供了中断异步操作的途径,但对于一个不具备 abort 能力的普通 Promise 来说,该怎么办?

对于这样的 Promise,我只能说,让他去吧,随便他去干到天荒地老 —— 反正我也没办法阻止。但生活总得继续,我不能一直等啊!

这种情况下我们可以把 setTimeout() 封装成一个 Promise,然后使用 Promise.race() 来实现“过时不候”:

race 是竞速的意思,所以 Promise.race() 的行为是不是很好理解?

function waitWithTimeout(promise, timeout, timeoutMessage = "timeout") {
    let timer;
    const timeoutPromise = new Promise((_, reject) => {
        timer = setTimeout(() => reject(timeoutMessage), timeout);
    }); 
    return Promise.race([timeoutPromise, promise])
        .finally(() => clearTimeout(timer));    // 别忘了清 timer
}

可以写一个 Timeout 来模拟看看效果:

(async () => {
    const business = new Promise(resolve => setTimeout(resolve, 1000 * 10));
    try {
        await waitWithTimeout(business, 1000);
        console.log("[Success]");
    } catch (err) {
        console.log("[Error]", err);    // [Error] timeout
    }
})();

以上就是JavaScript前端超时异步操作完美解决的详细内容,更多关于解决前端超时的异步操作的资料请关注猪先飞其它相关文章!

原文出处:https://blog.csdn.net/weixin_41996102/article/details/121357

相关文章

  • c# winform异步不卡界面的实现方法

    这篇文章主要给大家介绍了关于c# winform异步不卡界面的实现方法,文中通过示例代码介绍的非常详细,对大家学习或者使用c#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
  • 解决Java处理HTTP请求超时的问题

    这篇文章主要介绍了解决Java处理HTTP请求超时的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-29
  • C#多线程与异步的区别详解

    多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和异步操作的时机的区别...2020-06-25
  • Nodejs回调加超时限制两种实现方法

    这篇文章主要介绍了Nodejs回调加超时限制两种实现方法的相关资料,需要的朋友可以参考下...2017-06-15
  • C#异步的世界(下)

    这篇文章主要介绍了C#异步的世界(下),对异步感兴趣的同学,可以参考下...2021-04-26
  • 浅谈JS前端模块化的几种规范

    这篇文章主要介绍了JS前端模块化的几种规范,对前端模块化感兴趣的同学,可以参考下...2021-05-04
  • 又拍云异步上传实例教程详解

    这篇文章主要介绍了又拍云异步上传实例教程详解的相关资料,需要的朋友可以参考下...2016-04-23
  • Yii2.0中使用js异步删除示例

    本篇文章主要介绍了Yii2.0中使用js异步删除示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 ...2017-03-12
  • Spring @Async无法实现异步的解决方案

    这篇文章主要介绍了Spring @Async无法实现异步的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-02
  • asp.net使用H5新特性实现异步上传的示例

    下面小编就为大家分享一篇asp.net使用H5新特性实现异步上传的示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-09-22
  • C#百万数据查询出现超时问题的解决方法

    这篇文章主要介绍了C#百万数据查询出现超时问题的解决方法,是非常实用的技巧,需要的朋友可以参考下...2020-06-25
  • 详解前端安全之JavaScript防http劫持与XSS

    作为前端,一直以来都知道HTTP劫持与XSS跨站脚本、CSRF跨站请求伪造。防御这些劫持最好的方法是从后端入手,前端能做的太少。而且由于源码的暴露,攻击者很容易绕过防御手段。但这不代表我们去了解这块的相关知识是没意义的,本文的许多方法,用在其他方面也是大有作用。...2021-05-24
  • php实现异步将远程链接上内容(图片或内容)写到本地的方法

    这篇文章主要介绍了php实现异步将远程链接上内容(图片或内容)写到本地的方法,涉及php基于curl进行远程文件传输的相关操作技巧,需要的朋友可以参考下...2016-12-02
  • 前端jquery部分很精彩

    这篇文章主要为大家详细介绍了前端笔记之jquery部分,jQuery是一个兼容多浏览器的javascript库...2016-05-05
  • JQuery基于FormData异步提交数据文件

    这篇文章主要介绍了JQuery基于FormData异步提交数据文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-02
  • 使用kendynet构建异步redis访问服务

    这篇文章主要介绍了在kendynet上写的一个简单的redis异步访问接口,大家参考使用吧...2020-04-25
  • vue前端开发辅助函数状态管理详解示例

    vue的应用状态管理提供了mapState、mapGetters、mapMutations、mapActions四个辅助函数,所谓的辅助函数分别对State、Getters、Mutations、Actions在完成状态的使用进行简化...2021-10-10
  • 解决Shiro 处理ajax请求拦截登录超时的问题

    这篇文章主要介绍了解决Shiro 处理ajax请求拦截登录超时的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-06
  • C#前端验证和后台验证代码实例

    这篇文章主要介绍了C#前端验证和后台验证代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-11-03
  • php中session超时严格控制实例

    php中session默认是30分钟超时,但是有的时间压根就没到30分钟就自动超时了,这对很多操作带来不便,下面我们来看看解决30分钟超时的办法。 第一种回答 那么, 最常见的...2016-11-25