callback 和 promise 的错误捕获-暗坑集锦

wm1998 3年前
   <p> </p>    <p>最近忙于业务开发,好久没有更新博客了,把最近开发中踩到的关于错误捕获的坑,拿出来分享下;这些暗坑浪费了我大量的开发时间,只怪自己学识太浅。开始正文。</p>    <h2>callback</h2>    <p>运行 callbask.js</p>    <pre>  <code class="language-javascript">'use strict';    function cbAfter3s(callback){        setTimeout(function(){          // s2          try{              callback(null, '3s');          }catch(e){              console.error('Catch in cbAfter3s', e);              callback(new Error('Error from cbAfter3s'));          }            throw new Error('Error from cbAfter3s ASync');      }, 3e3);        throw new Error('Error from cbAfter3s Sync');  }    function handle(err, data){        console.info('Reveive: ', err, data);      if(!err){          // s2          throw new Error('Error from handle');      }  }      try{        cbAfter3s(handle);  }catch(e){      console.error('Catch in global', e);  }      process.on('uncaughtException', function(e){        console.error('Catch in process', e);  });  </code></pre>    <p>输出:</p>    <pre>  <code class="language-javascript">Catch in global [Error: Error from cbAfter3s Sync]    Reveive:  null 3s    Catch in cbAfter3s [Error: Error from handle]    Reveive:  [Error: Error from cbAfter3s] undefined    Catch in process [Error: Error from cbAfter3s ASync]    </code></pre>    <p>总结(s):</p>    <p>1. try catch 只能捕获同步抛出的错误</p>    <p>2. 不要轻易在 callback 里 throw 错误,不然容易形成两次回调。</p>    <p>3. 代码未捕获的错误,会出现在 uncaughtException 事件上,建议做些日志记录;不然,假如你用了进程守护程序(如pm2等),会自动重启应用,进而湮没日志。</p>    <p>4. promise 的错误捕获又是不同的,不能想当然。</p>    <h2>promise</h2>    <p>运行 promise.js</p>    <pre>  <code class="language-javascript">'use strict';    // 内置P romise  var p = (new Promise(function(resolve, reject){        reject(new Error('Error from promise by reject'));      // 或者通过 throw 的方式抛出,效果相同       // throw new Error('Error from promise by throw');    }));    // 或者在 then 通过 throw 抛出错误,也有同样效果  /**  var p = (new Promise(function(resolve){        resolve('Data');  }))  .then(function(res){      console.info('Receive: ', res);      throw new Error('Error from promise by throw');  });  */    process.on('uncaughtException', function(e){        console.error('UE:Catch in process', e);  });    process.on('unhandledRejection', (reason) => {        console.info('UR:Catch in process', reason);  });    process.on('rejectionHandled', (p) => {        console.info('RH:Catch in process', p);  });    setTimeout(function(){        p.catch(function(e){          console.error('Catch in Promise', e);      });  }, 1e3);  </code></pre>    <p>输出:</p>    <pre>  <code class="language-javascript">UR:Catch in process [Error: Error from promise by reject]    RH:Catch in process Promise { <rejected> [Error: Error from promise by reject] }    Catch in Promise [Error: Error from promise by reject]    </code></pre>    <p>总结(s):</p>    <p>1. rejectionHandled 事件的触发条件为, promise 没有被及时 catch 到错误并触发了 unhandledRejection 事件,在这之后的一段时间里, promise 错误又被处理了,此时触发 rejectionHandled ,详情见 <a href="/misc/goto?guid=4959672700030219761" rel="nofollow,noindex"> Node-Docs-4.4.1#process <em>event</em> rejectionhandled </a> 。</p>    <p>2. uncaughtException 并不能捕获 Promise 内抛出的错误,如果开发者是从基于 callback 的 <a href="/misc/goto?guid=4958824849782534716" rel="nofollow,noindex">Async</a> 转向 Promise 的,尤其需要注意未知错误的捕获。</p>    <p>由于历史代码历史包袱,有时我们会写一个 promiseToCallback 的函数,类似如下代码:</p>    <pre>  <code class="language-javascript">function promiseToCallback(func){        'use strict';      return function(){          let args = Array.prototype.slice.apply(arguments);          let cb = args.pop();          func.apply(null, args)              .then(function(result){                  cb(null, result);              })              .catch(function(err){                  log.error(err);                  cb(err);              });      };  };  </code></pre>    <p>这时候,尤其需要当心,cb 内如果抛出错误,或触发 catch 事件,导致发生两次回调,建议直接把 cb 的错误通过 try-catch 处理掉。</p>    <p>希望这些能让你在开发中少踩些坑。</p>    <p>来自: http://f2e.souche.com/blog/callback-he-promise-de-cuo-wu-bu-huo-an-keng-ji-jin/</p>