• promise高级用法: 同步执行,并发控制,并发控制并获取全部执行结果


    原文链接:https://www.cnblogs.com/yalong/p/16038528.html

    本文展示promise的三种用法

    1. promise实现异步代码的同步执行
    2. promise控制大量请求的并发执行
    3. promise控制大量请求的并发执行,并获取全部执行结果

    一.Promise实现异步代码的同步执行

    场景描述:

    有fn1 、fn2 、 fn3三个异步任务,要求必须先执行fn1,再执行fn2,最后执行fn3
    且下一次任务必须要拿到上一次任务执行的结果,才能做操作
    三个异步函数如下:

     // 异步函数fn1
     const fn1 = function () {
        return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('111')
        }, 500)
        })
      }
      
     // 异步函数fn2
     const fn2 = (data) => {
      return new Promise((resolve, reject) => {
       resolve(data + '222')
      })
     }
      
     // 异步函数fn3
     const fn3 = (data) => {
      return new Promise((resolve, reject) => {
       setTimeout(function () {
        resolve(data + '333')
       }, 1000)
      })
     }
    

    方法一:使用then的链式调用

      //链式调用
      fn1().then((data) => {
        return fn2(data)
      }).then((data) => {
        return fn3(data)
      })
      .then((data) => {
        console.log(data) // 111222333
      })
    

    方法二:使用async await

     async function queue(arr) {
        let res = null
        for (let promise of arr) {
          res = await promise(res)
        }
        return await res // 这里的await可以去掉,因为已经是最后一步了
     }
     
     
     // 因为async返回返回的也是promise,所以可以使用then
     queue([fn1, fn2, fn3])
        .then(data => {
          console.log(data) // 111222333
        })
    

    二. Promise并发控制

    场景描述一

    假设有100个ajax请求,每次发送3个请求,其中一个请求完毕,再加入新的请求,直到全部请求完毕
    如果使用promsie.all,浏览器会瞬间发送100个请求,这样可能会造成请求阻塞、页面卡顿、甚至服务器崩溃,显然不合理;
    那么就需要设计一个方案

    代码实现

      // 这个就是每次执行的异步请求方法,参数不一样
      const fn = (t) => {
        // 用setTimeout模拟异步请求
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('任务完成', t, new Date());
                resolve({ t, date: new Date() });
            }, t * 1000);
        })
      };
      
       let arr = [1, 1, 1, 2, 2, 2, 3, 3, 3]
    
    
      /**
       * arrs 请求数据源数组
       * limit 是每次并行发起多少个请求
       * handleFn 就是异步处理函数
      */
      function limitQueueFn(arrs, limit, handleFn) {
        // 完成任务数
        let index = 0;
        // 第一次的时候 一次性执行 limit 个任务
        for (let i = 0; i < limit; i++) {
            run();
        }
        // 执行一个任务
        function run() {
            // 构造待执行任务 当该任务完成后 如果还有待完成的任务 继续执行任务
            new Promise((resolve, reject) => {
                const value = arrs[index];
                index++; // 这个是同步操作
                // resolve 返回 promise
                resolve(handleFn(value))
            }).then(() => {
                if (index < arrs.length) {
                    run()
                }
            })
        }
      };
    
    
      limitQueueFn(arr, 3, fn)
    

    执行结果打印如下:

    任务完成 1 2022-03-22T03:19:17.907Z
    任务完成 1 2022-03-22T03:19:17.911Z
    任务完成 1 2022-03-22T03:19:17.911Z
    任务完成 2 2022-03-22T03:19:19.913Z
    任务完成 2 2022-03-22T03:19:19.914Z
    任务完成 2 2022-03-22T03:19:19.915Z
    任务完成 3 2022-03-22T03:19:22.919Z
    任务完成 3 2022-03-22T03:19:22.919Z
    任务完成 3 2022-03-22T03:19:22.919Z
    

    代码分析

    • 首先并行发起limit个请求
    • 然后就是利用promise.then, 当请求完成之后,就去发起下一个请求
    • 使用变量 index 来统计执行了多少个请求,没有执行就就一直执行

    场景描述二

    上面的代码是可以实现并发请求控制了, 但是如果想获取到全部请求执行完的结果,并且结果也要是有序的,跟 arrs 的顺序一样,这个怎么实现?

    代码实现

    const fn = (t) => {
      // 用setTimeout模拟异步请求
      return new Promise((resolve, react) => {
          setTimeout(() => {
              console.log('任务完成', t, new Date());
              resolve({ t, date: new Date() });
          }, t * 1000);
      })
    };
    
    let arr = [1, 1, 1, 2, 2, 2, 3, 3, 3]
    
    /**
     * arrs 请求数据源数组
     * limit 是每次并行发起多少个请求
     * handleFn 就是异步处理函数
    */
    function limitQueueFn(arrs, limit, handleFn) {
      // 完成任务数
      let runningIndex = 0; // 这是正在执行的下标
      let finishedIndex = 0 // 这是已经执行完的下表
      let result = new Array(arrs.length).fill(0) // 建立一个空数组, 存储结果
      return new Promise((resolveFn, rejectFn) => {
        // 第一次的时候 一次性执行 limit 个任务
        for (let i = 0; i < limit; i++) {
          run();
        }
        // 执行一个任务
        function run() {
            // 构造待执行任务 当该任务完成后 如果还有待完成的任务 继续执行任务
            new Promise((resolve, reject) => {
                const value = arrs[runningIndex];
                runningIndex++; // 这个是同步操作
                resolve(handleFn(value))
            }).then((res) => {
                result[finishedIndex] = res
                finishedIndex++
                if (runningIndex < arrs.length) {
                  run()
                } else { // 全部执行完毕
                  resolveFn(result)
                }
            })
        }
      })
    };
    
    limitQueueFn(arr, 3, fn).then(res => {
      console.log('结果如下:')
      console.log(res)
    })
    

    执行结果打印如下:

    任务完成 1 2022-03-22T03:18:10.420Z
    任务完成 1 2022-03-22T03:18:10.426Z
    任务完成 1 2022-03-22T03:18:10.426Z
    任务完成 2 2022-03-22T03:18:12.428Z
    任务完成 2 2022-03-22T03:18:12.430Z
    任务完成 2 2022-03-22T03:18:12.430Z
    任务完成 3 2022-03-22T03:18:15.435Z
    任务完成 3 2022-03-22T03:18:15.436Z
    任务完成 3 2022-03-22T03:18:15.436Z
    结果如下:
    [ { t: 1, date: 2022-03-22T03:18:10.426Z },
      { t: 1, date: 2022-03-22T03:18:10.426Z },
      { t: 1, date: 2022-03-22T03:18:10.426Z },
      { t: 2, date: 2022-03-22T03:18:12.429Z },
      { t: 2, date: 2022-03-22T03:18:12.430Z },
      { t: 2, date: 2022-03-22T03:18:12.430Z },
      { t: 3, date: 2022-03-22T03:18:15.436Z },
      { t: 3, date: 2022-03-22T03:18:15.436Z },
      { t: 3, date: 2022-03-22T03:18:15.436Z } ]
    
    参考文章:

    promise.all的实现
    promise 实现并发控制

  • 相关阅读:
    使用Code First Migrations依据代码更新数据库结构
    Engine Yard增加对Node.js的支持
    CSS3无前缀脚本prefixfree.js及Animatable介绍
    html5客户端本地存储之sessionStorage及storage事件
    cctype,string,vector
    管理朋友信息程序
    三位数的排列组合
    结构体字节对齐
    习题3.13
    OPENCV用户手册之图像处理部分(之一):梯度、边缘与角点(中文翻译)
  • 原文地址:https://www.cnblogs.com/yalong/p/16038528.html
Copyright © 2020-2023  润新知