Vue中通过Promise.all处理异步任务同时逐个延迟发送

        近期在项目开发中,遇到并发上报信息的问题。由于业务需要,要求同一时间将数组中的数据一起并且逐条发送给后台处理;在使用Promise.all处理多个异步任务时,发现1毫秒间十几条异步请求就发送完毕,导致服务器无法同时处理完毕这些任务,最终部分请求失败问题。

        所以在使用Promise.all的同时,也需要将每个异步任务的发送时间作个延迟处理。

一、了解Promise拓展方法

        在实现延迟效果前,先了解下Pomise实例的拓展方法,如下图:

序号名称描述
1Promise.all()所有的Promise对象均成功之后,才会执行then回调函数,否则执行catch回调函数。
2Promise.allSettled()确认一组异步操作是否都结束了(不管成功或失败),包含了”fulfilled"和"rejected"两种情况。
3Promise.any()ES2021引入了Promise.any()方法,其和all相反,所有的Promise对象均失败后才会执行catch回调函数,否则当任意一个异步任务成功后就会执行then回调函数。
4Promise.race()同样是多个Promise实例,不过一旦某个任务完成或失败,则会执行then回调函数或catch回调函数,并返回最先完成任务的响应结果。

1.1、Promise.all()示例

// 循环生成10条异步任务(模拟发送请求)
const requestList = Array.from({length: 10}).map((val, index) => {
    return new Promise((resolve, reject) => {
        // 最后一条发送失败结果
        index == 9 ? reject('error msg') : setTimeout(resolve, 1000, index);
    });
});

Promise.all(requestList).then(res => {
    console.log(res);
}).catch(msg => {
    console.error(msg);
});

        运行结果可看出,只要其中一个任务失败,则执行catch回调函数。全部任务执行成功才会执行then回调函数。如下图:

1.2、Promise.allSettled()示例

// 循环生成10条异步任务(模拟发送请求)
const requestList = Array.from({length: 10}).map((val, index) => {
    return new Promise((resolve, reject) => {
        // 最后一条发送失败结果
        index == 9 ? reject('error msg') : setTimeout(resolve, 1000, index);
    });
});

Promise.allSettled(requestList).then(res => {
    console.log(res);
}).catch(msg => {
    console.error(msg);
});

        从结果上可以看出,不管成功或失败,其都会执行then回调,并且成功的status状态值为fulfilled,失败的status状态值为rejected。如下图:

1.3、Promise.any()示例

// 循环生成10条异步任务(模拟发送请求)
const requestList = Array.from({length: 10}).map((val, index) => {
    return new Promise((resolve, reject) => {
        // 最后一条发送失败结果
        index == 9 ? reject('error msg') : setTimeout(resolve, 1000, index);
    });
});

Promise.any(requestList).then(res => {
    console.log(res);
}).catch(msg => {
    console.error(msg);
});

        其结果可见,只返回了一个成功的结果为0,从而得出结论:不管成功或失败,哪个任务最先执行完成,并会执行其回调结果。如下图:

1.4、Promise.race()示例

// 循环生成10条异步任务(模拟发送请求)
const requestList = Array.from({length: 10}).map((val, index) => {
    return new Promise((resolve, reject) => {
        // 最后一条发送失败结果
        index == 6 ? reject('error msg') : setTimeout(resolve, 1000, index);
    });
});

Promise.race(requestList).then(res => {
    console.log(res);
}).catch(msg => {
    console.error(msg);
});

        从结果中可见,多条异步任务中,只要有一个失败,则会执行catch回调函数。如下图:

1.5、any与race的相同点和区别

相同点:

  1. 都是返回最先处理完毕的任务响应结果。

区别:

  1. 二者都是返回最先执行完毕的任务的响应结果。
  2. any是不管成功或失败,只返回第一个执行完毕的结果;race则是有一个任务失败,只会返回失败的响应结果。
  3. any是等所有任务完毕后执行then或catch回调函数;race只要遇到失败任务,则会结束并执行catch回调函数。

1.6、race与all的相同点和区别

相同点:只要有任务失败,则执行catch回调函数。

区别:all全部成功,返回是所有任务的响应结果(数组形式);race只返回第一个完成任务的响应结果。

二、异步任务延迟处理

        在了解Promise的四种拓展方法后,根据本项目需求,则选用Promise.all()来完成所有的异步任务同时发送,并且对每个异步任务做延时发送处理。

2.1 引入api函数

        对于Axios的使用,这里不作细说,对axios的封装不清楚的朋友可以看下之前写过的一篇,地址:Vue.js快速入门之四:axios安装和使用_安装axios-CSDN博客

        在vue项目中,创建api/index.js用于定义接口请求函数,request.js为axios的封装文件。代码如下:

import Service from '@/utils/request'

export const sendMsg = ({text}) => {
    return Service({
        url: "/addCommon",
        method: 'post',
        params: {
            text
        }
    })
}

        页面中引入,代码如下:

<template>
  <div>
    <el-button type="info" size="small" style="margin-bottom: 20px;" @click="sendMsgEvent2">添加</el-button>
    <el-input type="textarea" rows="10" placeholder="请输入内容" v-model="commentText"></el-input>
  </div>
</template>

<script>
import { sendMsg } from '@/api/index'

export default { 

}

2.2 同时发送异步请求 

<template>
  <div>
    <el-button type="info" size="small" style="margin-bottom: 20px;" @click="sendMsgEvent">发送</el-button>
    <el-input type="textarea" rows="10" placeholder="请输入内容" v-model="commentText"></el-input>
  </div>
</template>

<script>
import { sendMsg } from '@/api/index'

export default {
  data(){
    return {
      commentText: ""
    }
  },
  methods: {
    sendMsgEvent(){
      if(this.commentText.trim() == '') {
        this.$message({type: "info", message: "请输入内容"});
        return;
      }
      let startTime = Date.now();
      // 循环追加共10个异步请求
      const reqs = Array.from({length: 10}).map(() => {
        return sendMsg({text: this.commentText, startTime})
      });
      // 通过Promise.all发送请求
      Promise.all(reqs).then(res => {
        console.log('ok', res);
      }).catch(e => {
        console.error('error', e);
      })
    }
    // end
  }
}
</script>

        start为所有异步任务发送时间,这么多条数据同时发送到服务端,会导致服务器负载增加,影响性能,可能导致服务器卡死或响应缓慢,造成部分请求失败等问题。结果如下图:

2.3 定义延迟执行函数

        所以既要满足业务需要,也要减轻服务端压力,则需要将多条异步任务进行延迟发送。在将数组中任务给到Promise.all之前,对应时行再加工。将每个异步任务,都依次序往后延迟100毫秒。代码如下:

export const delaySend = ({request, data}, index) => {
  return new Promise((resolve, reject) => {
    setTimeout(e => {
      // 追加开始时间,用于展示发送时的时间,实际项目中可去除
      data['startTime'] = Date.now();    
      // 执行接口函数request,并将入参带入
      request(e).then(resolve).catch(reject);
    }, 100 * index, data);
  })
}

        页面代码如下:

<template>
  <div>
    <el-button type="info" size="small" style="margin-bottom: 20px;" @click="sendMsgEvent">发送</el-button>
    <el-input type="textarea" rows="10" placeholder="请输入内容" v-model="commentText"></el-input>
  </div>
</template>

<script>
import { sendMsg } from '@/api/index'
import { delaySend  } from '@/utils/utils'

export default {
  data(){
    return {
      commentText: ""
    }
  },
  methods: {
    sendMsgEvent(){
      if(this.commentText.trim() == '') {
        this.$message({type: "info", message: "请输入内容"});
        return;
      }
      // 循环追加共10个异步请求
      const reqs =  Array.from({length: 10}).map(() => {
        return {request: sendMsg, data: {text: this.commentText}}
      }).map((item, index) => delaySend(item, index));
      // 通过Promise.all批量发送
      Promise.all(reqs).then(res => {
        console.log('ok', res);
      }).catch(e => {
        console.error('error', e);
      })
    }
    // end
  }
}
</script>

        这次结果可以看出,每个任务的开始发送时间,都是不一样的,基本都相差上百毫秒。这样服务器即可减轻服务端的压力,也能让所有请求全部得到响应,不会出现请求无响应问题了。结果如下图:

        注:diff为接口响应时间,结束时间和开始之差,即end-start=diff。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值