【JavaScript】forEach+异步操作,关于隐藏在循环逻辑中的语法坑

前言

书接上回使用dom-to-image-more生成页面截图,在实现了编辑器保存时截图并上传的功能后,连一刻都没有为需求的完成而摸鱼,立刻落到头上的工作是——修数。

虽说修数这种活一般情况下是后端负责的,但这次的业务场景比较特殊,需要前端渲染流程图后,再对页面进行截图。所以针对这次的修数工作,后端只能查询出需要修复的数据,再由前端在页面渲染流程图,并调用上传截图的接口。其中便涉及到在循环体中执行异步操作。


初次尝试

通过getServiceList接口查询出需要修正的数据treeInfoList 后,forEach遍历treeInfoList查询每一张流程图的详情信息,并在页面渲染后进行截图。

// 修正数据的方法
async reviseData() {
  // 遍历业务域信息列表
  this.businessDomainList.forEach(async (item) => {
  	console.log("step1");
  	// treeInfoList是需要修正的流程图数据,类型为数组
    const { treeInfoList } = await this.rpc.transactionDesign.getServiceList({
      turnPageShowNum: "9999", // 大力出奇迹,9999查全部
      beltLine: item.bizDomainNo,
    });
    console.log("step2");
    // 遍历数组,调接口查流程图详情→页面渲染→截图上传
    treeInfoList.forEach(async (data) => {
   	  console.log("step2.1");
      const res = await this.rpc.transactionDesign.getDecisionDetail({
        apiId: data.apiId,
      });
      console.log("step2.2");
      // 给相关的数据赋值,让页面渲染流程图
      this.dataObj = res;
      // 调用截图上传的方法
      await this.svg2Image();
      console.log("step2.3");
    })
  })
},

截图上传的方法,从上一篇文章copy,前情提要——使用dom-to-image-more生成页面截图

async svg2Image() {
  const node = document.querySelector(".dom_class");
  return domtoimage.toPng(node).then(async (dataUrl) => {
    const uploadFile = base64toFile(dataUrl, "坤图")
    // 通过formData保存文件
    const formData = new FormData();
    formData.append('uploadFile', uploadFile);
    // 设置其他附带参数
    formData.append('sing', this.sing);
    formData.append('dance', this.dance);
    formData.append('rap', this.rap);
    formData.append('basketball', this.basketball);
    const res = await this.rpc.babybaby.uploadAssetImage(formData);
    return res
  })
  .catch(function (error) {
    console.error('crush on you!', error);
  });
}

按照预想的逻辑进行,控制台打印的顺序应该是shep1→shep2→(shep2.1→shep2.2→shep2.3)* (treeInfoList.length - 1)→shep1→shep2→……,自信运行起来~

在这里插入图片描述
伴随着页面的一通变化,控制台的输出和预想的不能说一模一样,只能说毫无关系。稍作分析,不难察觉forEach循环体内对异步操作的处理有问题。通过面向度娘编程的方式查阅资料后,发现其中原因很简单,因为map/forEach内部使用了while和callback方式来执行函数。

我们可以参考下 Polyfill 版本的 forEach,简化以后类似就是这样的伪代码

while(index < arr.length) {
	callback(item, index)
}

从上述代码中我们可以发现,forEach 只是简单地执行了下回调函数而已,并不会去处理异步的情况。而且在 callback 中即便使用 break 也并不能结束遍历。

稍作修改

使用传统的for循环来代替forEach。

// 修正数据的方法
async reviseData() {
  // 遍历业务域信息列表
  for (let i = 0; i < this.businessDomainList.length; i++) {
    console.log("step1");
    // treeInfoList是需要修正的流程图数据,类型为数组
    const { treeInfoList } = await this.rpc.transactionDesign.getServiceList({
      turnPageShowNum: "9999", // 大力出奇迹,9999查全部
      beltLine: this.businessDomainList[i].bizDomainNo,
    });
    console.log("step2");
    // 遍历数组,调接口查流程图详情→页面渲染→截图上传
    for (let j = 0; j < treeInfoList.length; j++) {
      console.log("step2.1");
      const res = await this.rpc.transactionDesign.getDecisionDetail({
        apiId: treeInfoList[j].apiId,
      });
      console.log("step2.2");
      // 给相关的数据赋值,让页面渲染流程图
      this.dataObj = res;
      await this.svg2Image();
      console.log("step2.3");
    }
  }
}

自信运行2.0~
控制台的打印顺序是舒服了,但此时又出现了另一个问题——因为数据量较大,接口报错时很难定位到相应的ID。
在这里插入图片描述

加入错误处理

通过加入try/catch的逻辑,在接口报错的时候维护好相应的错误信息。

async reviseData() {
 // 遍历业务域信息列表
 for (let i = 0; i < this.businessDomainList.length; i++) {
   // treeInfoList是需要修正的流程图数据,类型为数组
   const { treeInfoList } = await this.rpc.transactionDesign.getServiceList({
     turnPageShowNum: "9999", // 大力出奇迹,9999查全部
     beltLine: this.businessDomainList[i].bizDomainNo,
   });
   // 遍历数组,调接口查流程图详情→页面渲染→截图上传
   for (let j = 0; j < treeInfoList.length; j++) {
     try {
       const res = await this.rpc.transactionDesign.getDecisionDetail({
         apiId: treeInfoList[j].apiId,
       });
       // 给相关的数据赋值,让页面渲染流程图
       this.dataObj = res;
       await this.svg2Image();
     } catch (error) {
       // 记录错误信息
       this.id2ErrorMessage[treeInfoList[j].apiId] = error;
       continue;
     }
   }
 }
 console.log("id2ErrorMessage", this.id2ErrorMessage)
}

在这里插入图片描述

总结

forEach 整个函数是同步执行的,它的实现是基于内部的回调函数执行,当进入循环之后,函数内部会去调用传进来的回调函数。当回调函数中存在异步操作时,就会被放入事件循环的异步队列中,但forEach 内部会继续去执行同步代码,也就是继续循环。

  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值