如何用Promise/async/await实现图片渲染完成后的加载状态控制?
用Promise/async/await重构图片加载逻辑,精准控制加载状态
我完全懂你的困扰——原来依赖最后一个setTimeout回调来关闭加载状态的方式太“凑活”了,根本没法保证图片真的加载并渲染完成。而且想用更清晰的Promise/async/await代替jQuery的Deferred语法,又不想大动原有DOM渲染的逻辑。下面就来一步步改造:
核心改造思路
- 把AJAX请求改成用
await处理,替代$.when().done() - 给每个图片的加载+动画过程封装成Promise,确保只有当图片真正加载完成、fadeIn动画结束后,才认为这个图片处理完毕
- 用
for循环+await替代批量setTimeout,让逐个加载图片的逻辑更直观 - 在点击事件里用
try/catch统一处理成功/失败场景,确保按钮状态总能恢复
改造后的完整代码
1. 点击事件改造(改成async函数)
$('#container').on('click', '#imgSearch', async function (e) { const $searchBtn = $(this); // 显示加载状态 $searchBtn.html(`<i class="fa fa-spinner fa-spin"></i>Loading`); // 保留你原来的单选按钮判断逻辑,这里用占位符代替 const search = /* 你的搜索关键词逻辑 */; const sort = /* 你的排序逻辑 */; const phpController = /* 你的接口地址 */; const limit = /* 数量限制 */; try { // 等待所有图片加载渲染完成 await showImgs(search, sort, phpController, limit); // 恢复按钮文本 $searchBtn.text('Search'); } catch (error) { // 出错时提示并恢复按钮 alert(error.message); $searchBtn.text('Search'); } });
2. 改造showImgs为async函数
async function showImgs(search, sort, phpController, limit) { imgArray = []; // 用await直接处理jQuery的AJAX请求($.post本身返回类Promise对象) const response = await $.post(phpController, { search, sort, limit }, { dataType: 'json' }); if (response.error) { // 抛出错误,让上层try/catch处理 throw new Error(response.error.img); } // 统一把结果转成数组,方便后续循环处理 const imgs = Array.isArray(response) ? response : [response]; // 逐个处理图片,每秒加载一个 for (let i = 0; i < imgs.length; i++) { // 等待对应时长的延迟(和原来的setTimeout逻辑一致) await new Promise(resolve => setTimeout(resolve, 1000 * i)); // 等待当前图片加载+渲染完成 await saveAndDisplayImage(imgs[i]); } }
3. 合并并改造图片处理函数(封装成async函数)
把原来的saveImages和displayImages合并,同时封装图片加载和动画完成的Promise:
async function saveAndDisplayImage(img) { imgArray.push(img); // 创建DOM元素 const $imgContainer = $(`<div id='${img.id}'><img src="${img.image}"></div>`); const $img = $imgContainer.find('img'); // 封装图片加载完成的Promise const imgLoadPromise = new Promise((resolve, reject) => { // 监听图片加载成功/失败 $img.on('load', resolve); $img.on('error', () => reject(new Error(`图片加载失败: ${img.image}`))); // 处理图片已缓存的情况 if ($img[0].complete) resolve(); }); // 添加到DOM并执行fadeIn,封装动画完成的Promise $imgContainer.hide().appendTo(imgSection); const fadeAnimationPromise = new Promise(resolve => $imgContainer.fadeIn(1000, resolve)); // 等待图片加载完成 AND 动画结束,才认为这个图片处理完毕 await Promise.all([imgLoadPromise, fadeAnimationPromise]); }
关键细节说明
- 为什么不用原来的alertFinished?:原来的逻辑只是等最后一个定时器触发,但图片可能还在加载、动画还没结束。现在通过
Promise.all同时等待图片加载和fadeIn完成,真正做到“渲染完成后再恢复按钮”。 - 关于DOM绘制事件的疑问:确实没有类似
readystatechange的全局事件专门监听DOM绘制完成,但对于单个元素来说,我们可以通过:- 图片元素的
load事件(监听资源加载完成) - jQuery动画的回调(监听视觉渲染动画结束)
- 复杂场景下可以用
requestAnimationFrame(监听浏览器下一次重绘)
来精准判断元素是否已经完成渲染。
- 图片元素的
- 最小改动原有逻辑:我保留了你原来的“每秒加载一个图片”“fadeIn动画”“图片数组存储”等逻辑,只是把异步部分封装成Promise,没有大改DOM渲染的代码。
内容的提问来源于stack exchange,提问作者jmoney
相关产品推荐
相关产品推荐

