将所有async代码编写在IIFE中是否存在弊端或副作用?
关于async IIFE写法的问题解答
一、当前写法的核心疏漏
- 没有异常处理:async 函数执行过程中只要任意一个
await的Promise抛出 rejection(比如网络请求失败、loader.getData报错),后续代码都会终止执行,你代码里的loaderAreaElem.style.display = 'none'永远不会触发,会导致加载动画一直显示,同时浏览器会抛出「未处理的Promise拒绝」错误。 - 没有防重复触发机制:用户快速多次点击按钮的话,会同时触发多个异步执行流,短时间内多次调用
loader.getData,还会导致列表内容重复插入。
二、长期使用这种写法的常见弊端
- 调试成本高:匿名的async IIFE在错误栈中只会显示为匿名函数,线上出问题时很难快速定位到对应的代码块。
- 逻辑无法复用:如果你其他地方也要用到相同的顺序请求逻辑,IIFE写在点击回调里没法直接复用,只能复制粘贴代码。
- 事件监听无法移除:如果你后续改用
addEventListener绑定事件,匿名的回调函数没有引用,没办法调用removeEventListener移除监听,容易造成内存泄漏。 - 代码可读性差:多层嵌套的匿名函数会让代码缩进变多,逻辑复杂的时候很难梳理执行流程。
三、是否需要专门声明命名函数?
不需要强制所有场景都用,根据你的实际需求决定即可,满足以下任意一种情况建议用命名函数:
- 同一段异步逻辑需要在多个地方调用
- 需要动态移除事件监听
- 异步逻辑代码量超过10行,需要提升可读性和调试效率
- 需要给函数加标识避免重复执行(比如给函数挂载执行状态锁)
优化后的写法参考
基础修复版本,解决异常处理和加载动画卡住的问题:
btnInOrderElem.onclick = () => { // 防重复点击判断 if (loaderAreaElem.style.display === 'inline') return (async () => { contentElem.innerHTML = ''; loaderAreaElem.style.display = 'inline'; addLi(await loader.getData()); addLi(await loader.getData()); addLi(await loader.getData()); })().catch(err => { // 可自定义错误提示逻辑 console.error('加载失败:', err) }).finally(() => { // 不管成功失败都关闭加载动画 loaderAreaElem.style.display = 'none'; }) }
如果需要复用或者方便调试,可以提取命名函数:
// 提取为独立的命名异步函数 async function loadListData() { if (loaderAreaElem.style.display === 'inline') return contentElem.innerHTML = ''; loaderAreaElem.style.display = 'inline'; try { addLi(await loader.getData()); addLi(await loader.getData()); addLi(await loader.getData()); } catch (err) { console.error('加载失败:', err) } finally { loaderAreaElem.style.display = 'none'; } } btnInOrderElem.onclick = loadListData // 后续移除监听也很方便:btnInOrderElem.onclick = null // 用addEventListener绑定的话:btnInOrderElem.addEventListener('click', loadListData) // 对应移除逻辑:btnInOrderElem.removeEventListener('click', loadListData)
内容的提问来源于stack exchange,提问作者Edward Tanguay
相关产品推荐
相关产品推荐

