【问题标题】:JavaScript Promise : Catching errors within chained functionsJavaScript Promise:在链式函数中捕获错误
【发布时间】:2021-05-31 00:35:05
【问题描述】:

我有一个用于向 IndexDb 数据库添加记录的函数:

async function addAsync(storeName, object) {
    return new Promise((res, rej) => {
        // openDatabaseAsync() is another reusable method to open the db.  That works fine.
        openDatabaseAsync().then(db => {
            var store = openObjectStore(db, storeName, 'readwrite');
            var addResult = store.add(JSON.parse(object));
            addResult.onsuccess = res;
            addResult.onerror = (e) => {
                console.log("addResult Error");
                throw e;
            };
        }).catch(e => {
            // Error from "throw e;" above NOT GETTING CAUGHT HERE!
            console.error("addAsync ERROR > ", e, storeName, object);
            rej(e);
        });
    })
}

如果我尝试添加重复键,那么我期望:

addResult.onerror = (e) => {
    console.log("addResult Error");
    throw e;
}

捕捉它。确实如此。

但是,我也期待我的

.catch(e => {
    // Error from "throw e;" above NOT GETTING CAUGHT HERE!
    console.error("addAsync ERROR > ", e, storeName, object);
    rej(e);
})

捕获该错误。但相反,我得到了一个“未捕获”的日志。

控制台输出:

addResult Error
Uncaught Event {isTrusted: true, type: "error", target: IDBRequest, currentTarget: IDBRequest, eventPhase: 2, …}

最后的 .catch 是否只处理来自 openDatabaseAsync 调用的异常?我现在会想到,因为它被链接到 .then.

总之,我对上述代码的期望如下:

  • 如果 openDatabaseAsync() 失败,那么我没有发现它,因此错误将被发送给 addAsync() 的调用者。
  • 如果 .then 失败,那么我希望 .catch 能够捕获它,记录错误,然后拒绝承诺,这意味着调用 addAsync() 需要处理。

但是,我认为我应该从该行获取日志:

console.error("addAsync ERROR > ", e, storeName, object);

在拒绝被发送回 addAsync() 的调用者之前,此时可能未处理。

【问题讨论】:

  • 没有被捕获的东西是addAsync().catch() 的东西,因为你拒绝了从addAsync() 返回的承诺。你知道那个电话的结果吗?
  • 谢谢@TKoL。您的问题的答案是“否”。它由 Blazor JSInterop 调用,但我可以继续使用它。但是,就您而言,我没有得到日志"addAsync ERROR > ...",所以这告诉我throw e; 没有被.catch 捕获,这就是我目前感到困惑的地方。随后我可以从addAsync() 的调用者继续进行外部处理。

标签: javascript promise


【解决方案1】:

您的方法将受益于更大的检修。

  • 一般来说,当函数不使用await时,不要写成async
  • 不要将new Promise() 用于返回承诺的操作,例如openDatabaseAsync()。回复该承诺,或切换到async/await
  • 包装 IndexedDB 操作以使其遵循 Promise 语义会很有用。

IDBRequest为例:

function promisifyIDBRequest(idbr) {
    return new Promise( (resolve, reject) => {
        idbr.onsuccess = () => resolve(idbr.result);
        idbr.onerror = (e) => reject(e.target.error);
    });
}

现在你可以这样做了:

async function addAsync(storeName, object) {
    const db = await openDatabaseAsync();
    const store = openObjectStore(db, storeName, 'readwrite');
    return promisifyIDBRequest(store.add(JSON.parse(object)));
}

如果您想处理 addAsync() 内部的错误,请添加一个 try/catch 块。

值得一试现有的解决方案,这些解决方案使用承诺语义封装了整个 IndexedDB 接口,例如 https://github.com/jakearchibald/idb


FWIW,上述函数的 promise-chain 变体如下所示:

function addAsync(storeName, object) {
    return openDatabaseAsync().then( (db) => {
        const store = openObjectStore(db, storeName, 'readwrite');
        return promisifyIDBRequest(store.add(JSON.parse(object)));
    });
}

两种变体都返回一个 IDBRequest 结果的承诺。

【讨论】:

  • 非常感谢!里面有一些值得深思的地方。一个问题。这似乎是一个通用的“承诺者”。因此,我是否应该发送函数而不是结果,即return await promisifyIDBRequest(() => store.add(JSON.parse(object)));
  • 或者第一个代码块应该是:idbr.onsuccess = () => resolve(idbr.result)?那么这个 promisifyIDBRequest 函数是所有具有“onsuccess”和“onerror”属性的 IDB 请求的通用处理程序?
  • @NeilW 不,promisifyIDBRequest() 期望 store.add() 返回的内容作为输入,是的,它可能是任何具有 onsuccess/onerror 接口的通用承诺符,但在此在特殊情况下,onsuccess 处理程序期望 irbr.result 用于承诺解析,因此它也与之相关。
  • 我有。再次感谢。除了整理我的 IndexedDb 交互之外,我试图做我在这里挖掘的原因是因为我没有通过 JSInterop 将 JS 错误信息返回到 .NET。另一个关键的学习是,当拒绝来自 .onerror 的承诺时,我最好拒绝 (e.target.error) 而不仅仅是 (e)。否则 .NET Exception.Message 只是[object Event]!再次感谢。
  • @NeilW 是的,这是多余的。 return await somePromise();return somePromise() 本质上是相同的操作,但return await 为整体操作增加了一个额外的步骤。甚至有一个 ESLint 规则:no-return-await。只要您在addAsync 中添加try/catch 块,它就会变得有用,在jakearchibald.com/2017/await-vs-return-vs-return-await 中进行了很好的概述。
猜你喜欢
  • 2015-07-21
  • 2019-02-17
  • 1970-01-01
  • 1970-01-01
  • 2014-09-18
  • 2021-04-21
  • 2017-05-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多