javascript - 异常处理,抛出的错误, promise 内

标签 javascript node.js promise

我正在运行外部代码,作为对node.js服务的第三方扩展。 API方法返回 promise 。已解决的 promise 意味着成功执行了操作,失败的 promise 意味着执行操作存在一些问题。

现在这是我遇到麻烦的地方。

由于第3方代码未知,因此可能存在错误,语法错误,类型问题以及可能导致node.js引发异常的多种情况。

但是,由于所有代码都包装在Promise中,因此这些抛出的异常实际上会以失败的Promise的形式返回。

我试图将函数调用放在try/catch块中,但从未触发过:

// worker process
var mod = require('./3rdparty/module.js');
try {
  mod.run().then(function (data) {
    sendToClient(true, data);
  }, function (err) {
    sendToClient(false, err);
  });
} catch (e) {
  // unrecoverable error inside of module
  // ... send signal to restart this worker process ...
});

在上面的伪代码示例中,当引发错误时,它将在失败的promise函数中出现,而不是在catch中。

根据我的阅读,这是 promise 的功能,而不是问题。但是,我很难理解为什么总是要对待异常和期望的拒绝完全一样。

一种情况是关于代码中的实际错误,可能是无法修复的,另一种情况是可能丢失了配置信息,参数或可恢复的东西。

谢谢你的帮助!

最佳答案

崩溃并重新启动进程不是处理错误甚至错误的有效策略。在Erlang上没问题,那里的过程很便宜,并且做一件孤立的事情,例如为单个客户服务。这不适用于节点,在节点上,流程的成本增加了几个数量级,并一次为成千上万的客户提供服务
假设您的服务每秒处理200个请求。如果其中的1%到达了代码中的抛出路径,则每秒将导致20个进程关闭,大约每50毫秒关闭一次。如果您有4个核心,每个核心1个进程,则将在200ms内丢失它们。因此,如果一个进程启动并准备服务请求所需的时间超过200毫秒(对于不加载任何模块的节点进程,最低成本约为50毫秒),那么我们现在已经成功地实现了全面的拒绝服务。更不用说用户遇到错误了,往往会做类似重复刷新页面,从而使问题更加复杂。
域无法解决问题,因为它们是cannot ensure that resources are not leaked
#5114#5149问题上了解更多信息。
现在,您可以尝试对此“变得聪明”,并基于一定数量的错误制定某种流程回收策略,但是无论采用哪种策略,都会严重改变节点的可伸缩性配置文件。我们每个进程每秒要处理几十个请求,而不是数千个。
但是,promise会捕获所有异常,然后以与同步异常在堆栈中向上传播非常相似的方式传播它们。另外,他们经常提供finally方法,相当于try...finally。由于这两个功能,我们可以通过构建“上下文管理器”来封装该清理逻辑(类似于python中的withC#中的usingtry-with-resourcesJava)中总是会清理资源。
假设我们的资源用acquiredispose方法表示为对象,这两种方法均返回promise。调用函数时未建立任何连接,我们仅返回资源对象。稍后将由using处理此对象:

function connect(url) {
  return {acquire: cb => pg.connect(url), dispose: conn => conn.dispose()}
}
我们希望API像这样工作:
using(connect(process.env.DATABASE_URL), async (conn) => {
  await conn.query(...);
  do other things
  return some result;
});
我们可以轻松实现以下API:
function using(resource, fn) {
  return Promise.resolve()
    .then(() => resource.acquire())
    .then(item => 
      Promise.resolve(item).then(fn).finally(() => 
        // bail if disposing fails, for any reason (sync or async)
        Promise.resolve()
          .then(() => resource.dispose(item))
          .catch(terminate)
      )
    );
}
使用fn参数返回的promise链完成后,将始终处理资源。即使在该函数(例如从JSON.parse)或其内部.then闭包(如第二个JSON.parse)中引发了错误,或者链中的promise被拒绝(等同于调用了错误的回调)。这就是为什么它对于 promise 捕获错误并传播错误如此重要的原因。
但是,如果处置资源确实失败,那确实是终止的充分理由。在这种情况下,我们极有可能泄漏了资源,并且开始关闭该过程是一个好主意。但是现在,我们崩溃的机会被隔离到了代码的一小部分,即实际上处理可泄漏资源的那部分!
注意:终止基本上是带外抛出的,所以 promise 不能捕获它,例如process.nextTick(() => { throw e });。哪种实现才有意义可能取决于您的设置-基于nextTick的实现的工作方式类似于回调如何保释。
如何使用基于回调的库?它们可能是不安全的。让我们看一个示例,看看这些错误可能来自何处以及哪些可能导致问题:
function unwrapped(arg1, arg2, done) {
  var resource = allocateResource();
  mayThrowError1();
  resource.doesntThrow(arg1, (err, res) => {
    mayThrowError2(arg2);
    done(err, res);
  });
}
mayThrowError2()在内部回调中,即使抛出,即使在另一个promise的unwrapped中调用了.then,它也会使进程崩溃。这类错误通常不会被典型的promisify包装程序捕获,并且将继续照常导致进程崩溃。
但是,如果在mayThrowError1()中调用.then,将被promise捕获,并且内部分配的资源可能会泄漏。
我们可以编写一个偏执的promisify版本,以确保所有引发的错误均不可恢复,并导致进程崩溃:
function paranoidPromisify(fn) {
  return function(...args) {
    return new Promise((resolve, reject) =>   
      try {
        fn(...args, (err, res) => err != null ? reject(err) : resolve(res));
      } catch (e) {
        process.nextTick(() => { throw e; });
      }
    }
  }
}
如果在未包装的抛出情况下在另一个promise的.then回调中使用promisified函数,则会导致进程崩溃,并返回到throw-crash范式。
人们普遍希望,当您使用越来越多的基于promise的库时,它们将使用上下文管理器模式来管理其资源,因此,您无需再让进程崩溃。
这些解决方案都不是防弹的-甚至不会因抛出的错误而崩溃。意外地编写即使不抛出资源也会泄漏资源的代码非常容易。例如,此节点样式函数即使不抛出也会泄漏资源:
function unwrapped(arg1, arg2, done) {
  var resource = allocateResource();
  resource.doSomething(arg1, function(err, res) {
    if (err) return done(err);
    resource.doSomethingElse(res, function(err, res) {
      resource.dispose();
      done(err, res);
    });
  });
}
为什么?因为当doSomething的回调收到错误时,代码会忘记处理资源。
上下文管理器不会发生这种问题。您不能忘记调用dispose:您不必这样做,因为using为您做到了!
引用:why I am switching to promisescontext managers and transactions

关于javascript - 异常处理,抛出的错误, promise 内,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19528705/

相关文章:

javascript - 动态添加 Angular UI 日期选择器

javascript - 覆盖 AngularJs 服务

javascript - 如何在 .EJS 模板内添加变量

node.js - 使用 Nodejs 和 body-parser 发布表单数据

javascript - 异步/等待 promise 逻辑问题

Ruby - 延迟计算哈希

javascript - 转换为 mp3 完成后如何解决此 promise ( Fluent-ffmpeg )

javascript - 使用 AJAX Jquery 处理随机链接数

javascript - 将 JSON 数据从 Python 发送到 Javascript 并访问它们。

javascript - 与 java 相比, Node 中的 AES 加密输出不同