背景

在公司遇到了一个需求,后端在处理文件的时候前端采用轮询的方式来获取文件的状态,现在的问题是,如果后端处理的时间过长,前段用户等待时间过长,因此我们需要给用户一个弹窗来提示用户处理时间过长。

原代码

 //定义获取函数
 async function getMessage(info, times = 0) {
    // 获取后端信息
    let code = message.code;
    try {
      //code为1表示还在处理中 
      if (code == 1) {
        await new Promise((resolve, reject) => {
          //开启定时器进行轮询
          setTimeout(async () => {
            await getMessage(info, times + 1);
            resolve()
          }, 2000);
        });
      }
    } catch (e) {
      console.log('处理出错');
    }
  }


  //执行代码
  async function init(){
    try{
      await getMessage()
    }catch(e){
      console.log('外部接受错误');
    }
  } 
  init()

一代错误代码

一开始自己写,想通过reject进行终止,代码如下

async function getMessage(info, times = 0) {
    // 获取后端信息
    let code = message.code;
    console.log('开始处理');
    try {

      //code为1表示还在处理中
      if (code == 1) {
        await new Promise((resolve, reject) => {
          //开启定时器进行轮询
          setTimeout(async () => {
            //已经轮询100
            if (times >= 5) {
              reject("超时");
              return;
            }
            await getMessage(info, times + 1);
            resolve();
          }, 2000);
        })
      }
    } catch (e) {
      console.log("处理出错");
      throw '扔错'
    }
  }


  async function init(){
    try{
      await getMessage()
    }catch(e){
      console.log('外部接受错误');
    }
  } 
  init()
/*
输出:
6 * 开始处理
处理错误
Uncaught (in promise) 扔错
*/

二代错误代码

这个代码仍然报错,报的依然是 Uncaught (in promise) 扔错

之后我们在处理出错那里打断点,发现确实有错误经过console.log(处理出错)

(真的踩了很久的坑

这时同事提醒,在Promise对象后面catch试试

async function getMessage(info, times = 0) {
    // 获取后端信息
    let code = message.code;
    console.log('开始处理');
    try {

      //code为1表示还在处理中
      if (code == 1) {
        await new Promise((resolve, reject) => {
          //开启定时器进行轮询
          setTimeout(async () => {
            //已经轮询100
            if (times >= 5) {
              reject("超时");
              return;
            }
            await getMessage(info, times + 1);
            resolve();
          }, 2000);
        }).catch(()=>{
          throw 'PromiseCatch错误'
        })
      }
    } catch (e) {
      console.log("处理出错");
      throw '扔错'
    }
  }


  async function init(){
    try{
      await getMessage()
    }catch(e){
      console.log('外部接受错误');
    }
  } 
  init()

/*
输出:
6 * 开始处理
处理错误
Uncaught (in promise) 扔错
*/

最终代码

我们重新分析一下这个代码,首先从报错开始看,Uncaught (in promise),我们可以定位到,这一定是Promise的错误,其次再来看Promise中的await,它用来执行自己(请求函数),这个请求函数如果出错每次都会抛出一个错误,但是我们的Promise中并没有处理这个错误。因此Promise对象报错,所以我们应该为await包裹一个try catch,用于处理Promise返回的错误,并且reject掉,不能throw Error,详细可以看这篇文章 js中对于返回Promise对象的语句如何try catch

  async function getMessage(info, times = 0) {
    // 获取后端信息
    let code = message.code;
    console.log('开始处理');
    try {

      //code为1表示还在处理中
      if (code == 1) {
        await new Promise((resolve, reject) => {
          //开启定时器进行轮询
          setTimeout(async () => {
            //已经轮询100
            if (times >= 5) {
              reject("超时");
              return;
            }
            try{
              await getMessage(info, times + 1);
            }catch(e){
              reject("超时")
            }
            resolve();
          }, 2000);
        }).catch(()=>{
          throw 'PromiseCatch错误'
        })
      }
    } catch (e) {
      console.log("处理出错");
      throw '扔错'
    }
  }


  async function init(){
    try{
      await getMessage()
    }catch(e){
      console.log('外部接受错误');
    }
  } 
  init()
/*
6 开始处理
6 处理出错
外部接受错误
*/

后记

其实解决这个问题的方案也很多,我当时也考虑了另一种方案,就是通过添加返回值来判断,但被驳回了(也不算驳回?只是可能会改动比较多的地方,也不太保险,而且这个问题确实没有特别难)

Q.E.D.