为什么 VS Code 会因 Reject in Promise 处理的异常而中断?

Oli*_*n04 3 javascript exception node.js promise visual-studio-code

以这段代码为例,我们有一个 Promise 调用一个会失败的函数,它应该将错误传递给 Promise 的 catch 方法。
从终端运行时它工作得很好。但是,当运行 vscode 时,它​​会在(1)处爆炸。

function failingFunc() {
    let undef = undefined;
    return undef.nope();
}
let promise = new Promise((resolve, reject) => {
   resolve(failingFunc()); // (1) Explodes when run from vscode
});
promise.then(v => {}).catch((e: Error) => {
    console.log(e.message); // (2) Prints when run from the terminal
});
Run Code Online (Sandbox Code Playgroud)

为什么是这样?

vscode 关于页面:
版本 1.14.2
提交 cb82feb
日期 2017-07-19T23:26:08.116Z
Shell 1.6.6
Renderer 56.0.2924.87
Node 7.4.0

Oli*_*n04 6

解决方案

正如@TJCrowder在评论中指出的那样:

只有在附加处理程序之前抛出异常导致拒绝时才会发生这种情况。例如,这不会导致它,因为当异常转换为拒绝时,已经附加了一个拒绝处理程序:

new Promise((resolve, reject) => setTimeout(() => { 
    try { 
        throw new Error(); 
    } catch (e) { 
        reject(e); 
    } 
}, 0)).catch(error => console.log("Error:", error));
Run Code Online (Sandbox Code Playgroud)

事实证明,这是使用 vscode 调试 Nodejs 时的一个已知“错误”。正如它在这个问题中所解释的(在 vscode git 存储库中),这是因为 Nodejsundefined在遇到reject回调时会发送一个带有异常的中断事件。当 vscode 的调试器看到这个中断事件时,它会对未知异常执行它应该做的事情,它会暂停执行然后抛出异常。
更多关于这个问题(在 vscode-node-debug2 存储库)@roblourens说:

如果在附加错误处理程序之前拒绝承诺,即使只检查“未捕获的异常”,调试器也会中断。如果在附加错误处理程序后它被拒绝,它会按预期工作。真正的问题是 promise 不知道它的拒绝是否会被处理。

您仍然可以使用 vscode 开发基于 Promise 的系统,但是您需要关闭 vscode 中的所有错误处理,如下所示,确保没有勾选任何选项。 注意:由于这远非最佳解决方案,因此将来可能会更改和/或改进。

(我已经对vscode 问题发表了评论,如果我学到任何有用的东西,我会更新这篇文章)

Edit1:
我发现另一种解决方法是在 vscode 中定义一个键绑定来运行命令workbench.action.debug.run。这将运行当前选定的调试选项,而不将调试器附加到它。这意味着您可以将调试器保持在您的正常设置上,同时在需要处理被拒绝的承诺时使用新的键命令运行代码。

/* keybindings.json */
[
    {
        "key": "ctrl+shift+b",
        "command": "workbench.action.debug.start"
        /* Attaches debugger */
    },
    {
        "key": "ctrl+b",
        "command": "workbench.action.debug.run"
        /* Runs without debugger */
    }
]
Run Code Online (Sandbox Code Playgroud)

Edit2:
正如@TJCrowder在评论中指出的那样:

只有在附加处理程序之前抛出异常导致拒绝时才会发生这种情况。例如,这不会导致它,因为当异常转换为拒绝时,已经附加了一个拒绝处理程序:

new Promise((resolve, reject) => setTimeout(() => { 
    try { 
        throw new Error(); 
    } catch (e) { 
        reject(e); 
    } 
}, 0)).catch(error => console.log("Error:", error));
Run Code Online (Sandbox Code Playgroud)

当然他是对的。下面的代码在附有调试器的 vscode 中确实有效。

function failingFunc() {
    let undef = undefined;
    return undef.nope();
}
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        try {
            resolve(failingFunc())
        } catch (e) {
            reject(e);
        }
    }, 0);
});
promise.then(v => {}).catch((e: Error) => {
    console.log(e.message); // Cannot read property 'nope' of undefined
});
Run Code Online (Sandbox Code Playgroud)