JavaScript循环内用async-await处理fetch 如何等待所有异步完成后执行富文本粘贴
问题根源
- 现有循环逻辑属于无等待的异步触发流程:循环内调用的
fetchBlob、fetchUrl是基于Promise的异步任务,但全程没有通过await阻塞等待执行结果,循环体不会卡在异步步骤上,会瞬间遍历完所有图片节点,直接执行后续的编辑器粘贴逻辑。 - 所有网络请求都是后台并发发起,因此控制台会先打印全部
loop end、execute html日志,等请求全部返回触发.then回调后,才会打印image日志。 - 代码中存在几处冗余/错误:
- 两个异步处理函数定义在循环内部,每遍历一个图片就重新声明一次,造成不必要的内存开销
fetchUrl内部已经通过response.json()将响应解析为JS对象,后续再调用JSON.parse(json)会抛出类型错误- 通过
tmp.querySelectorAll('img')[i]回查DOM的方式容易因为索引错位(比如跳过无src的图片节点)匹配错元素
调整后的实现
优先推荐并发处理所有图片的方案(比逐张串行等待效率更高),核心逻辑是把每一张图片的处理任务封装成Promise收集成数组,通过Promise.all统一等待所有任务完成后,再执行编辑器粘贴动作。
调整后的完整代码如下:
// 通用工具函数移到循环外部统一声明,避免重复定义 async function fetchBlob(src) { const response = await fetch(src); if (!response.ok) throw new Error('图片源拉取失败'); return response.blob(); } async function uploadImage(file) { const formData = newTokenData(); formData.append("image", file); const response = await fetch('upload-image', { method: "POST", body: formData }); // 直接返回解析后的JSON对象,无需后续二次parse return response.json(); } const forLoop = async _ => { // 收集所有图片处理任务的Promise const imageTasks = []; for (let i = 0; i < imgNodeList.length; i++) { const img = imgNodeList[i]; if (!img.src) continue; // 封装单张图片的全流程处理逻辑 const singleTask = (async () => { try { // 1. 从源URL拉取图片Blob const blob = await fetchBlob(img.src); const file = new File([blob], "new_file"); // 2. 上传图片到自有服务器 const uploadRes = await uploadImage(file); console.log('image'); // 3. 上传成功后替换临时DOM中的图片地址 if (uploadRes.status == '200') { // 直接操作当前循环拿到的img对象,避免回查DOM导致索引错位 img.src = uploadRes.url; } } catch (err) { // 单张图片处理失败不阻断整体流程,输出错误日志即可 console.error('单张图片处理失败', err); } })(); imageTasks.push(singleTask); } // 阻塞等待所有图片处理任务执行完成(包含成功、失败的所有状态) await Promise.all(imageTasks); // 所有图片地址替换完成后,再执行编辑器粘贴逻辑 console.log('execute html'); quill.clipboard.dangerouslyPasteHTML(index, tmp.innerHTML); } forLoop();
关键调整点说明
- 移除了循环内无等待的
.then链式调用,所有异步任务都被转化为可被等待的Promise对象 - 通过
await Promise.all(imageTasks)确保所有图片拉取、上传、DOM替换动作全部完成后,才会执行粘贴步骤,解决了先粘贴、后等图片上传的时序问题 - 修复了原代码中重复声明函数、重复解析JSON、DOM查询索引错位的潜在bug
- 增加单任务异常捕获,避免某一张图片处理失败导致整个流程无响应
- 移除了原代码中冗余的
isJson判断逻辑,因为response.json()本身会自动校验响应格式,解析失败会触发异常进入捕获逻辑
内容的提问来源于stack exchange,提问作者user11543803
相关产品推荐
相关产品推荐

