延迟解决始终在尝试捕获范围内

Deferred resolution always within try catch

本文关键字:范围内 解决 延迟      更新时间:2023-09-26

我正在使用jQuery Deferred的独立实现(git repo)。保持问题简单,如果我进行var status = _.Deferred()并从任何函数中返回status.promise(),那么我是否必须为该函数中的所有步骤添加一个 try catch 以reject延迟错误?

从返回 promise 的函数中抛出将在返回 promise 之前发生,因此,no:

function foo() {
  var status = _.Deferred();
  throw 'DOH!';
  return status.promise();  // this will never execute
}

您遇到麻烦的地方是在 promise 返回函数中启动的异步调用是否可以抛出:

function bar() {
  throw 'DOH!';
}
function foo() {
  var status = _.Deferred();
  setTimeout(function () {
    status.resolve(bar());
  }, 0);
  return status.promise();
}
foo().
  fail(function () {
    // *not* invoked when bar throws
  });

在这种情况下,您需要将调用包装为 bar

function bar() {
  throw 'DOH!';
}
function foo() {
  var status = _.Deferred();
  setTimeout(function () {
    try {
      status.resolve(bar());
    } catch (e) {
      status.reject(e);
    }
  }, 0);
  return status.promise();
}
foo().
  fail(function () {
    // invoked when bar throws
  });

但是,接受回调的异步函数应捕获自己的错误,并将结果或错误传递给回调。

如果异步函数返回 promise,则不需要捕获,实际上也不需要创建自己的Deferred

function bar() {
  var d = _.Deferred();
  setTimeout(function () {
    d.resolve(42);
  }, 0);
  return d.promise();
}
function foo() {
  return bar().
    then(function (result) {
      return result * 2;
    }).
    then(function (result) {
      if (result === 84) { // true
        throw 'DOH!';
      }
    ));
}
foo().
  then(function (result) {
    // *not* invoked
  }).
  fail(function (e) {
    console.log(e.message); // 'DOH!'
  });

在javascript中,try/catch不像Java那样是一种生活方式。原因是javascript在避免可预测类型的错误方面具有相当丰富的方法。

拒绝延期并非绝对必要。通常,如果需要执行某些fail代码,或者需要主动防止以后解决延迟的某些事件,则会这样做。

人们很容易认为未解决/未被拒绝的延期将永远挂起。情况不一定如此。延迟可用于垃圾回收,此时在任何范围内都不存在对它的引用,无论是直接的还是通过其承诺。

人们也很容易认为已解决/拒绝的延迟会自动进行垃圾回收。这也是不真实的。如果对延迟的引用仍然存在,在任何范围内,那么它仍将存在于内存中,并且仍然有用,因为观察程序方法(例如 done/fail/always/then)可能会在以后被调用(并可能立即触发)。

简而言之,关于 GC,Deferreds 就像任何其他 js 对象一样,尽管通常使用它们的代码结构使得很难发现它们何时可用于 GC。