JS promise限制异步并发数 原创 精华

中软小助手
发布于 2022-3-31 09:56
浏览
2收藏

前言

作者:王珮云
前端开发中经常遇到加载的图片太多或者太大导致页面加载完成慢的问题,图片太多导致向服务器请求的次数太多,图片太大导致每次请求的时间过长,本次将介绍一种通过控制请求数提高加载体验的方案。

Promise 限制异步并发数,尽快完成异步请求

  1. 场景:页面加载图片过多时,加载速度慢,容易卡顿
  2. 思路: 假设异步并发数为3
    比如先请求前三个(下标0,1,2),并且使用Promise.Race同时请求,其中一个完成,则把已经完成请求的换成还没请求的(下一个索引为3),直到遍历完,将最后三个未完成的用Promise.all来请求。

先带大家回顾下Promise

  1. 从本意看,就是一个承诺,是承诺就会有一个结果,所以它有三个状态:pending(正在进行中),fulfilled(完成承诺),rejected(没完成承诺),一旦状态改变就不会再变,用Promise可以解决回调地狱的问题.Promise本身不是异步,但是可以解决异步问题,并且通过then可以实现链式调用,可以支持多个并发请求并且获取并发请求中的数据,用维护状态和传递状态的方式使得回调能够及时被调用,比使用callback更加简单灵活。

举个例子:

JS promise限制异步并发数-鸿蒙开发者社区
从上图示例可以看出,then中接受两个参数,分别是成功的回调,另一个是失败的回调,这里只处理了成功的回调,Promise状态改变为fulfilled,虽然 aaa 这个变量没有定义,但是也没报错,而是继续往下执行在catch中捕获了错误,如果不用Promise,这里一定会报错。

Promise.race([request1,request2,request3,…]),就是谁先执行完成就以谁为准执行回调,传入的参数是一个Promise数组
在本示例中对应的就是参数就是 promises,它相当于一个容器,里面每个Promise状态都是pending,谁先改变状态就先执行谁,执行成功会返回对应的索引,这个索引值后面会用到。

JS promise限制异步并发数-鸿蒙开发者社区

Promise.all([request1,request2,request3,…]),跟Promise.race传参方式一样,可以并行处理多个异步请求,不过是要等所有的异步请求完成后才执行回调,在处理数据较少或者需要预先加载所有资源文件才能达到效果的场景时使用,本次示例是在处理最后一组数据时使用到的。

代码展示

 let urls = [
             './imgs/a.png',
             './imgs/b.png',
             './imgs/c.png',
             './imgs/d.png',
             './imgs/e.png',
             './imgs/f.png',
         ];

 //promise异步请求图片
 function loadImg(url) {
     return new Promise((resolve, reject) => {
         const img = new Image();
         img.onload = function () {
             console.log('当前图片加载完成', img);
             resolve(img);
         };
         img.onerror = function () {
             reject(new Error('加载错误'));
         };
         img.src = url;
         img.style.width = 200 + 'px';
         document.body.appendChild(img);
         document.body.style.textAlign = 'center';
     });
 }

 function limitLoad(urls, handler, limit) {
     let sequence = [].concat(urls); //  拷贝一份urls
     
     // 初始化Promise容器,请求前三个
     let promises = sequence
         .splice(0, limit)
         .map((url, index) => {
             return handler(url).then(() => {
                 return index;
             });
         });

     //定义reduce的函数累加器,pCollect为初始值也是最后的返回值,通过Promise.resolve()进行初始化
     function getTotalFunc(pCollect, url) {
         return pCollect
             .then(() => {
                 return Promise.race(promises); //返回已经完成的下标
             })
             .then((fastestIndex) => {
                 //接收已经完成的下标值,将容器中已经完成的一项替换
                 promises[fastestIndex] = handler(url).then(() => {
                     return fastestIndex;
                 });
             })
             .catch((err, url) => {
                 console.log(err, url);
             });
         }
         //遍历剩余url,并返回在外部实现链式调用
         return sequence
             .reduce(getTotalFunc, Promise.resolve())
             .then(() => {
                 //剩余最后三个用all处理
                 Promise.all(promises);
         });
     }

 // 最后一步:调用
 limitLoad(urls, loadImg, 3)
     .then((res) => {
         console.log('图片加载完成', res);
     })
     .catch((err) => {
         console.error(err);
     });

效果展示

JS promise限制异步并发数-鸿蒙开发者社区

执行过程

JS promise限制异步并发数-鸿蒙开发者社区

总结:

实际开发中图片过多会大大影响页面加载速度,影响用户体验,使用这种方法可以达到减少请求时间,优化传输速度的目的,是提高加载速度的方法之一。

更多原创内容请关注:中软国际 HarmonyOS 技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
1
收藏 2
回复
举报
1条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

Promise讲的很透彻,学到了

回复
2022-3-31 10:28:12
回复
    相关推荐