Promise的错误处理


距离上一次写 blog 已经又过去了两月有余,一周一篇的目标很难到达呀,借口就不找了,尽力弥补吧。

今天我们来说一说 Promise 的错误处理,随着 ES6(aka ES2015) 开始普及,以及越来越多的 Nodejs 应用出现,Promise 作为处理异步的一种重要工具,在我们日常的工作中使用也越来越频繁,本文不准备就 Promise 的基本概念及功能作一些描述,推荐两篇文章(MDN Promise, We have a problem with promises中文)足以让你对Promise非常了解。

我们今天专门讲一下 Promise 的错误处理。

promise.then 方法接收两个回调函数为参数,其中第一个为成功回调函数,第二个为失败回调函数,需要注意的是 这儿的两个回调函数基于promsie这个 Promise 实例的处理结果的,比如:

Promise.reject(new Error('jser down'))
  .then(() => {
    console.log('never here')
  }, (err) => {
    console.log(err.message) // jser down
  })

如果你想捕捉 then 的成功回调函数的错误,那需要再写一个 then 加上失败的错误回调,简而言之就是:then 里的错误处理函数是对上一个处理过程的错误捕捉。

那么,对于一个常见的错误处理可能是这样的:

Promise.resolve('a:1')
  .then(JSON.parse)
  .then((json) => {
    console.log(json)
  }, (err) => {
    //这儿捕获的是上一个JSON.parse时的错误
    console.log(err.message) //Unexpected token a
  })

常见的一级一级的异步处理任务,用这种错误捕捉代码就显得太难看了,打码效率也低效了,应好有 catch 来统一处理:

Promise.resolve('{"a":1}')
  .then(JSON.parse)
  .then((json) => {
    return Promise.resolve(json.a++)
  })
  .then((a) => {
    return Promise.reject(new Error('error 1'))
  })
  .then(() => {
    return Promise.reject(new Error('error 2'))
  })
  .catch((err) => {
    console.log(err.message) //error 1
  })

catch 很方便的解决了我们对于多级 Promise 处理的问题,那对于一些忘记加或者无法加 catch 的情况怎么处理呢?庆幸的是 API 的设计者们又为我们这些应用开发者想好了,有一个全局的事情叫 unhandledRejection 这个事件在浏览器( Chrome 49+ )和 Nodejs 里名称稍微有点不一样,浏览器里是全小写的 unhandledrejection 。 比如:

process.on('unhandledRejection', function (reason, promise) {
  console.log(reason) // [Error: unhandledrejection]
  console.log(promise) // Promise { <rejected> [Error: unhandledrejection] }
})

setTimeout(() => {
  Promise.reject(new Error('unhandledrejection'))
}, 1000)

在浏览器里,我们需要 window.addEventListener("unhandledrejection", (e)=>{}) ,在 Web Workers 里需要使用 self.addEventListener,我们来写一个简单的小模块,把 Nodejs 、浏览器以及 Web Workers 里的情况统一一下:

const onUnhandledRejection = (function () {
  const isNode = typeof process === 'object' &&
    Object.prototype.toString.call(process) === '[object process]'

  if (isNode) {
    return function (cb) {
      process.on('unhandledRejection', cb)
    }
  }

  return function (cb) {
    self.addEventListener('unhandledrejection', (ev) => {
      cb(ev.reason, ev.promise)
    })
  }
})()

/* 使用:
onUnhandledRejection((r,p) => {
  console.log(r)
})
*/

OK ,今天的小文就到此为止啦。


推荐文章