【问题标题】:Catch an error inside of Promise resolver在 Promise 解析器中捕获错误
【发布时间】:2017-05-06 09:30:51
【问题描述】:

我无法捕捉到 Resolve 承诺中发生的异常/错误。 有人可以解释一下是否有办法捕捉到这个错误:

createAsync = function() {
    var resolve, reject,
    promise = new Promise(function(res, rej) {
        resolve = res;
        reject = rej;
    });
    promise.resolve = resolve;
    promise.reject = reject;
    return promise;
};

promise = createAsync()
promise.then(function myresolver(){
    throw Error('wow')
})


// Catching the error here
try {
  promise.resolve()
} catch(err) {
  console.log( 'GOTCHA!!', err );
}

编辑:

让我更好地解释一下,我正在尝试创建一个 API,而用户只能访问 promise resolve/reject 部分:

// This is my API user does not have access to this part
promise = createAsync()
promise
.then(function(fun) {
    if (typeof fun != 'function')
        throw Error('You must resolve a function as argument')
}).catch(function(err){
    // Some internal api tasks...
    return Promise.reject(err)
})

现在我想给他解决方案,但不起作用:

// Here is where the user can resolve the promise.
// But must be able to catch the error
promise.catch(function(err){
    console.log("GOTCHA", err)
})
promise.resolve('This must be a function but is a string');

有什么想法吗?


更多信息: Yes 是常见用法的反模式。这是一个远程过程调用 API,因此用户必须能够拒绝或解决。而且调用是异步的,所以最好的方法是使用 Promise。我不会说这种情况下的反模式。实际上是唯一的模式。 (我不能使用 Async/Await)

【问题讨论】:

  • // Catching the error here 不,你不是 - 你promise.resolve() 不会拒绝承诺......你的promise.then ... throw 将“拒绝”那个承诺(由.then 返回的那个)而不是promise中的一个
  • “导出”提供给执行程序的 resolvereject 函数(传递给 promise 构造函数的函数),在您的情况下,通过使用其他属性污染 promise 对象,是一种反图案。 Promise 构造函数设计的全部要点是解析和拒绝在执行器内部是隔离的。您的方法允许任何碰巧获得您的承诺对象之一的随机人解决或拒绝它,这几乎肯定不是好的程序设计。
  • @EnZo torazaburo 是对的,您应该始终将解析能力与承诺分开。通常你不希望用户调用resolvereject - 更好的承诺模式是简单地接受用户的承诺,他可以自行解决/拒绝。
  • “用户”坐在 RPC 的哪一侧?如果您可以显示实际代码(一些 API 对象?)而不是 createAsync 伪代码,这可能会有所帮助。
  • 哦,你是对的,真正的代码复杂的。不幸的是,我无法弄清楚f 中的localProcedureCall 是什么以及它如何使用/需要使用req 对象。但是,我可以告诉你 localProcedureCall 不应该接受 resolvereject 回调,而是 return 一个承诺(你可能是 want to use .then(…, …) instead of .then(…).catch(…)),并且 dop.protocol.subscribe 的模式基本上应该看起来像return new Promise(resolve => storeRequest(node, request_info, resolve); })

标签: javascript promise


【解决方案1】:

让我更好地解释一下,我正在尝试创建一个 API,而用户只能访问 promise resolve/reject 部分。 现在我想给他的解决方案不起作用。 有什么想法吗?

不要将所有内容都放在最初的 promise 对象上。听起来您真正需要向用户展示的是 a) 解决方法(包括拒绝)和 b) 获取结果(承诺)的方法。所以你需要做的就是给他一个函数:

function createAsync() {
    var resolve;
    var promise = new Promise(function(r) { resolve = r; })
    .then(function(fun) {
        if (typeof fun != 'function')
            throw Error('You must resolve a function as argument')
    }).catch(function(err){
       // Some internal api tasks...
       throw err;
    });
    return function(value) {
        resolve(value);
        return promise;
    };
}

他会像这样使用它

var start = createAsync();
// possibly later
start('This must be a function but is a string').catch(function(err){
    console.log("GOTCHA", err)
});
// or
start(Promise.reject(new Error('doomed to fail'))).catch(…);

但实际上这种模式仍然令人费解且过于复杂。只需给您的createAsync 函数一个参数(如果用户需要延迟传递fun,它可能会收到一个承诺)并完成它:

function createAsync(valu) {
    return Promise.resolve(val).then(function(fun) {
        if (typeof fun != 'function')
            throw Error('You must resolve a function as argument')
    }).catch(function(err){
       // Some internal api tasks...
       throw err;
    });
}

createAsync('This must be a function but is a string').catch(function(err) {
    console.log("GOTCHA", err)
});
// or
createAsync(new Promise(function(resolve) {
    // later
    resolve('This must be a function but is a string');
})).catch(function(err) {
    console.log("GOTCHA", err)
});

【讨论】:

  • createAsync 实例必须在内部创建。我不能允许用户创建承诺,因为我需要实例在内部解决。
  • 看来还是要等用户拨打resolve,应该没什么区别吧?但如果你必须这样做,只需使用前两个 sn-ps 中的模式。
  • 如果您可以共享/链接您的真实代码,这可能会有所帮助。
  • 首先,两个sn-ps不允许用户拒绝。真正的代码非常复杂。编辑后的例子是最简单准确的理解方式。
  • 确实如此,请参阅第二个 sn-p 的最后一行。
【解决方案2】:

警告:正如@torazaburo 所指出的,“导出”提供给执行程序的解析和拒绝函数(传递给承诺构造函数的函数),在你的情况下,通过使用附加属性污染承诺对象,是反模式。 Promise 构造函数设计的全部要点是解析和拒绝在执行器内部是隔离的。您的方法允许任何碰巧获得您的承诺对象之一的随机人解决或拒绝它,这几乎肯定不是好的程序设计。

考虑到这一警告,您对promise.resolve() 的调用不会拒绝该承诺......您的promise.then ... throw 将“拒绝”该承诺(由.then 返回的那个)而不是promise 中的那个

重写你的代码如下:

promise = createAsync()
promise // the call to promise.resolve will resolve THIS promise, so .then will execute
.then(function myresolver(){
    throw Error('wow')
}) // .then returns a NEW promise, in this case rejected with Error('wow')
.catch(function(e) {
    console.log("GOTCHA", e)
});

promise.resolve()

您会在控制台中看到预期的 GOTCHA

【讨论】:

  • ES7 的 async 函数在异步编程方面倒退了一步:p - 另外,这是对代码的巨大改变,而不是答案中相对较小的改变
  • 当然,但这只是您的主观意见,而不是重点:)。一个客观的事实是,您可以将try/catch 与它们一起使用。是的,代码需要完全改变,所以我说“值得指出”:)。我想“值得一提”会更准确。
  • 事情就是这样,我什至不知道如何重写该代码以使用 async/await + try/catch。虽然我理解(并且已经使用过)异步/等待,但我仍然是树林里的宝贝:p 欢迎您教育我:p
  • @guest271314 我的意思是一个友好的建议,伙计们:P。根据要求,我已经为此发布了自己的示例
  • @nem035 - 根本不认为这个建议是不友好的 - 顺便感谢你的课程
【解决方案3】:

Jaromanda's answer 已经说明您必须使用拒绝处理程序或catch 来使用 Promise 处理异步错误。

还请务必阅读 torazaburo 的评论,其中解释了为什么您的方法是反模式。

如果你真的必须使用这种方法并且出于某种原因还需要try/catch,ES7 会通过async functions 为你提供一种方法

(async function f() {
  const createAsync = function() {
    var resolve, reject,
      promise = new Promise(function(res, rej) {
        resolve = res;
        reject = rej;
      });
    promise.resolve = resolve;
    promise.reject = reject;
    return promise;
  };

  const promise = createAsync();
  promise.resolve();

  // Catching the error here
  try {
    await promise.then(function() {
      throw Error('wow')
    });
  } catch (err) {
    console.log('GOTCHA!!', err);
  }
})()

【讨论】:

  • 抱歉,我不能使用 async/await。
  • 哦,这很有趣 - 对不起@nem035 - 但你教了我一件事,所以不是在静脉中
  • 我看不出有任何理由将 createAsync 放在 IIAFE 中。而且你不需要命名f
  • 您是绝对正确的,但不确定您的总体观点是什么,因为这些都是不会有太大变化的小细节。我的例子只是为了演示 try/catch 的承诺,就他的代码而言,其他人已经提出了更好的方法。
猜你喜欢
  • 2020-01-11
  • 2015-07-21
  • 2019-02-05
  • 2016-10-26
  • 1970-01-01
  • 2021-04-21
  • 2015-11-06
  • 1970-01-01
  • 2019-04-07
相关资源
最近更新 更多