在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
自从 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 确实可以处理超时,只需要在 try {...} catch (err) { if (err.isAxiosError && !err.response && err.request && err.message.startsWith("timeout")) { // 如果是 Axios 的 request 错误,并且消息是延时消息 // TODO 处理超时 } } finally {...} Axios 没问题了,如果用 处理 fetch() 超时
如果需要中断一个 const ac = new AbortController(); const { signal } = ac; fetch(url, { signal }).then(res => { // TODO 处理业务 }); // 1 秒后取消 fetch 操作 setTimeout(() => ac.abort(), 1000);
上面这个示例演示了如何实现 const ac = new AbortController(); const { signal } = ac; setTimeout(() => ac.abort(), 1000); const res = await fetch(url, { signal }).catch(() => undefined); 为了避免使用 本来到这里就可以结束了,但是对每一个 async function fetchWithTimeout(timeout, resoure, init = {}) { const ac = new AbortController(); const signal = ac.signal; setTimeout(() => ac.abort(), timeout); return fetch(resoure, { ...init, signal }); } 没问题了吗?不,有问题。 如果我们在上述代码的 setTimeout(() => { console.log("It's timeout"); ac.abort(); }, timeout); 并且在调用的给一个足够的时间: fetchWithTimeout(5000, url).then(res => console.log("success")); 我们会看到输出 对了,我们虽然为 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 都提供了中断异步操作的途径,但对于一个不具备 对于这样的 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前端超时异步操作完美解决的详细内容,更多关于解决前端超时的异步操作的资料请关注极客世界其它相关文章! |
请发表评论