promise链如何处理错误?

678 阅读3分钟

promise中抛出的错误经常会遇见,下面探讨几种常见的出现promise的错误的情况,以及如何处理

  1. 当一个promise出现错误时,控制权将移交给最近的reject程序进行处理。
  fetch('https://no-such-server.blabla') // reject
  .then(response => response.json())
  .catch(err => alert(err)) // TypeError: Failed to fetch(这里的文字可能有所不同)

这里当无法解析数据报错时,首先catch回调先执行捕获错误。

  1. promise 的执行者(executor)和 promise 的处理程序周围有一个“隐式的 try..catch”。当主动抛出错误时,后面的catch会捕获到。
new Promise((resolve,reject)=>{
    throw new Error('error')
  }).catch((err)=>{
    console.log(err);
  })

上述代码类似于下面:

  new Promise((resolve,reject)=>{
    reject(new Error('error'))
  }).catch((err)=>{
    console.log(err);
  })

无论是否主动reject 只要throw抛出错误,都会被下面的catch捕获到

对于所有的 error 都会发生这种情况,而不仅仅是由 throw 语句导致的这些 error。例如,一个编程错误:

new Promise((resolve, reject) => {
    resolve('ok')
}).then((res) => {
    test() // 此處不存在這個函數
}).catch((err) => {
    console.log(err);
})

也会被错误捕获。

  1. 再次抛出错误
new Promise((resolve, reject) => {

    throw new Error("Whoops!");
  
  }).catch(function(error) {
  
    alert("The error is handled, continue normally");
  
  }).then(() => alert("Next successful handler runs"));

注意: 此时执行到catch之后 仍然会继续执行then回调。除非在catch 里面主动抛出throw错误,那么then回调就不会被执行,错误继续由下一个catch捕获。promise链继续向下执行。

例子:

// 执行流:catch -> catch-> then
new Promise((resolve, reject) => {
    throw new Error("Whoops!");
}).catch(function (error) {
    alert("The error is handled, continue normally");
    throw new Error('fff')
}).then(() => {
    // 這裡不會被執行
    alert("Next successful handler runs")
}).catch((err) => {
    alert(err)
}).then(() => {
    console.log('end');
});

执行结果为:

  • The error is handled, continue normally
  • fff
  • end

catch 后面有then 则会继续执行,catch后面没有then 则执行到catch为止。

  1. promise链中未处理的错误
new Promise((resolve,reject)=>{
  // resolve('ggg')
  reject('hhhhhhhhh')
}).catch(()=>{
  console.log('yyyyyyyyyyy')
}).then(()=>{
  console.log('iiiiiiiiiiiiiii')
})

image.png

catch后面没有then 执行到catch为止

new Promise((resolve,reject)=>{
  // resolve('ggg')
  reject('hhhhhhhhh')
}).then(()=>{
  console.log('iiiiiiiiiiiiiii')
}).catch(()=>{
  console.log('yyyyyyyyyyy')
})

image.png

window.addEventListener('unhandledrejection', function(event) {
    // 这个事件对象有两个特殊的属性:
    alert(event.promise); // [object Promise] —— 生成该全局 error 的 promise
    alert(event.reason); // Error: Whoops! —— 未处理的 error 对象
  });
  
  new Promise(function() {
    throw new Error("Whoops!");
  }); // 没有用来处理 error 的 catch

对于promise链中没有处理的错误,一般可以全局监听unhandledrejection事件,来捕获错误。

  1. promise链代码周围有个隐式的 try..catch,所以所有的同步代码都可以得到处理。但是对于异步代码的话就没办法处理了
new Promise(function(resolve, reject) {
    setTimeout(() => {
      throw new Error("Whoops!");
    }, 1000);
  }).catch(alert);

这里由于setTimeout是个异步任务里面的宏任务,因此里面抛出去的错误无法被隐式的try catch 捕获到。因此无法被处理,所以在catch函数里面就无法被执行到。

7 async await 实现异步操作

主要用来简化promise链 使异步操作看起来像同步操作 一般使用try catch 来捕获异步操作

const sleep = () => new Promise((resolve)=>{
  setTimeout(resolve, 1000); 
})
const exection = async ()=>{
  try {
      await sleep()
  } catch (error) {
    throw new Error(error)
  }
}
exection()

但其实await本身也可以实现捕获错误 catch 链实现

const sleep = () => new Promise((resolve)=>{
  setTimeout(resolve, 1000); 
})
const exection = async ()=>{
  // try {
  //     await sleep()
  // } catch (error) {
  //   throw new Error(error)
  // }
  const res = await sleep().catch((error)=>{
    throw new Error(error)
  })
  console.log(res)
}
exection()

这里如果sleep 出现错误的话 错误将会由catch捕获 如果正常的话 res 可以拿到正常的值。

如果使用then 继续取值的话 res 将会取不到值 由then 取值

const sleep = () => new Promise((resolve)=>{
  setTimeout(()=>{
    resolve('success')
  }, 1000); 
})
const exection = async ()=>{
  // try {
  //     await sleep()
  // } catch (error) {
  //   throw new Error(error)
  // }
  const res = await sleep().catch((error)=>{
    throw new Error(error)
  }).then((res)=>{
    console.log(res) // 这里success
  })
  console.log(res) // 这里undefined 取不到值
}
exection()