js的异步函数执行转为同步函数执行

15,563 阅读3分钟

使用场景:A函数向后台请求得到数据dataA,B函数需要利用到数据dataA立即进行一些计算或者向后台请求别的数据,这时候写

this.A();
this.B();

会报错,因为异步的原因,执行B的时候A的数据还没返回。

方法1

首先执行的函数A:

A() {
      const params = {
        projectName: this.listSearchKey
      };
      // 利用promise
      return new Promise((resolve, reject) => {
        // 异步请求
        projectList(params)
          .then(res => {
            if (res.code === '0') {
              this.dataA = res.data;
              // 返回想要的数据
              resolve(this.dataA);
            }
          })
          .catch(err => {
            console.error(err);
          });
      });
}

接着执行的函数B

B(id) {
    //用到了函数A的返回数据dataA,并且希望拿到返回数据之后立即执行
      const params = {
        projectId: id || this.dataA[0].projectId
      };
      taskList(params)
        .then(res => {
          if (res.code === '0') {
            this.tableData = res.data.list;
            this.total = res.data.total;
          }
        })
        .catch(err => {
          console.error(err);
        });
    },

在执行时这样写

A().then(val => {
      B();
    });

方法2

async function asyncMain(){
    try{
        let result =await A();
        result = await B();
    }catch(err){
        console.error(err);
    }
}
asyncMain()

完整的异步转同步实现

想要实现真正的异步转同步实现,可以选择使用promise或者其 all/race、async/await。如果单纯的相互依赖的接口只有两个接口,那么只需要按照上述操作即可。

但是如果存在多个相互依赖的接口,那么可能需要使用promise.all/race或者async/await

需要注意的是,如果使用上述方法,那我们就必须让每一个接口函数返回的是 promise 对象,返回promise.resolve 对象便于让程序继续执行,promise.reject可以让 promise.all/race 或者 async/await 更好的捕捉错误。

那么,完成这样的工作需要以下几步:

第一步:让每一个异步函数返回 promise 对象,并且有条件的返回 resolve 或者 reject;

// 接口1
handleData() {
  return new Promise((resolve, reject) => {
    taskDetail()
      .then(res => {
        // code非0的时候,表明接口报错,需要reject
        if (res.code !== '0') {
          reject(res.msg);
          return;
        }
        // 当前为正常状态,resolve即可
        this.ftpInfoOptions = res.data;
        resolve();
      })
      .catch(err => {
        // catch 时也需要reject
        return reject(err);
      });
  });
},
// 接口2
getSystemOptions() {
  const params = {
    apsRole: 0,
    taskType: this.taskType
  };
  return new Promise((resolve, reject) => {
    getApSystemByApsRoleAndTaskType(params)
      .then(res => {
        if (res.code !== '0') {
          reject(res.msg);
          return;
        }
        this.systemOptions = res.data;
        resolve(res.data);
      })
      .catch(err => {
        return reject(err);
      });
  });
},

第二步,确认多个异步函数之间的依赖关系,选择方法。

上述两个接口是,详情接口以及获取接收方接口,当然详情接口依赖于多个接口,在此只举例一个接口;

公司的业务流程时,编辑某条消息时必须在详情 之前获取很多接口,以便于让数据回显,我们选择了使用async/await,除了彼此依赖的关系以及有先后顺序,还会考虑到如果前置接口异常,剩下的接口就没有必要调用了。

新建某条消息又会考虑到,某一个接口不应该堵塞掉后面的接口,所以选择了promise.all。

// 写在created里面
async created() {
    // 判断是否为编辑态
    const isEdit = this.taskId !== `null`;
    try {
      // 这两个接口无论编辑新建,都是前置条件,必须在最前面调用,有先后顺序
      await this.getSystemOptions();
      await this.getReceiverOptions();
      // 详情接口,编辑才调用
      isEdit && (await this.handleData());
    } catch (error) {
      // 承接接口的 reject
      console.error(error);
    }
    
    
    // 如果有异步操作在进行之前,必须拿到其他多个异步接口的数据且不分先后,则选择all的方法
    Promise.all([
      !isEdit ? this.getFtpInfo('add') : '',
      !isEdit ? this.getCstoreInfo({}) : '',
    ])
      .then(() => {
        // 拿到之后的操作
    })
      .catch(error => {
        // 如果两个接口报错,只能catch到一次
        console.error(error);
      });
  },