【问题标题】:Getting UnhandledPromiseRejectionWarning though catching rejections as good as possible获得 UnhandledPromiseRejectionWarning 尽管尽可能好地捕捉拒绝
【发布时间】:2018-04-17 16:00:08
【问题描述】:

我知道 SO 上涉及UnhandledPromiseRejectionWarning 的问题的现有答案,但找到的答案似乎都与我的情况不相符。基本上,我以为我知道如何处理承诺。但是这个案例让我发疯了,所以我想在这里寻求一些支持:如何摆脱那个警告?

我正在 NodeJS 中使用以下_write() 实现实现一些可写流:

_write( data, encoding, doneFn ) {
    Promise.race( [
        this.getSocket(),
        new Promise( ( resolve, reject ) => {
            setTimeout( reject, 1000, Object.assign( new Error( `timeout on trying to connect w/ ${this.address}` ), { code: "ECONNREFUSED" } ) );
        } ),
    ] )
        .then( socket => {
            if ( socket ) {
                return new Promise( ( resolve, reject ) => {
                    socket.once( "error", reject );
                    socket.write( data, encoding, () => {
                        socket.removeListener( "error", reject );

                        resolve();
                    } );
                } );
            }
        } )
        .then( doneFn )
        .catch( error => {
            const { address } = this;

            switch ( error.code ) {
                case "EPIPE" :
                case "ECONNRESET" :
                    Log( `lost connection w/ ${address.id}` );
                    break;

                case "ECONNREFUSED" :
                    Log( `failed to connect w/ ${address.id}` );
                    break;
            }

            console.log( "error is", error.message );
            doneFn( error );
        } );
}

当我测试这段代码在超时时正确发出错误时,我得到了神秘的UnhandledPromiseRejectionWarning

error is timeout on trying to connect w/ /ip4/127.0.0.1/tcp/1
(node:5052) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: timeout on trying to connect w/ /ip4/127.0.0.1/tcp/1

由于_write() 在设计上没有任何承诺,我很确定这个警告是由于这里给出的代码。但我不知道如何摆脱警告。即使明确附加 catch-handler 也不会抑制它。

更新:代码已经过重构和重新测试。尽管如此,仍会记录拒绝警告。我最初使用的是 NodeJS 6.11,但这次是 8.11。

【问题讨论】:

  • 如果您重构代码以删除承诺构造函数反模式会发生什么 - 即new Promise( ( writeResolve, writeReject ) => {
  • @JaromandaX 我不明白你的意思。你想让我从代码中删除所有承诺吗?那将不是解决方案,而是一种解决方法。由于使用了对 Promises 的原生支持(没有 bluebird 或类似的),因此没有机会创建没有构造函数模式的 Promises。
  • 试试catch all unhandled promises,它会告诉你是哪一行代码在抛出它。
  • 不,我只是认为您可以通过删除冗余的 Promise 构造函数来简化代码,该构造函数包装了已经处理 Promise 的代码 - 这是该反模式的问题之一 - 它可能会导致一些事情像这样
  • @JaromandaX 现在我明白了。 ;) 我会试一试的。

标签: javascript node.js promise


【解决方案1】:

看起来流实现与Promise.race() 存在问题。

我试图将代码分解为基本模式:

function someHandler( doneFn ) {
    const toBeResolvedLate = new Promise( ( resolve, reject ) => {
        setTimeout( resolve, 5000 );
    } );
    const toBeRejectedEarly = new Promise( ( resolve, reject ) => {
        setTimeout( reject, 1000, new Error( "timeout" ) );
    } );

    Promise.race( [
        toBeRejectedEarly,
        toBeResolvedLate,
    ] )
        .then( doneFn )
        .catch( error => {
            doneFn( error );
        } );
}


someHandler( ( ...args ) => {
    console.log( "directly:", args );
} );


const { Writable } = require( "stream" );

const stream = new Writable( {
    write: function( a, b, done ) {
        someHandler( ( ...args ) => {
            console.log( "in _write:", args );
            done( ...args );
        } );
    },
} );

stream.write( Buffer.from( "chunk" ) );

结果我得到了这个:

directly: [ Error: timeout
    at Promise (...\scratch_2.es6:6:30)
    at new Promise (<anonymous>)
    at someHandler (...\scratch_2.es6:5:29)
    at Object.<anonymous> (...\scratch_2.es6:20:2)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10) ]
in _write: [ Error: timeout
    at Promise (...\scratch_2.es6:6:30)
    at new Promise (<anonymous>)
    at someHandler (...\scratch_2.es6:5:29)
    at Writable.write [as _write] (...\scratch_2.es6:29:4)
    at doWrite (_stream_writable.js:397:12)
    at writeOrBuffer (_stream_writable.js:383:5)
    at Writable.write (_stream_writable.js:290:11)
    at Object.<anonymous> (...\scratch_2.es6:36:9)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10) ]
(node:3288) UnhandledPromiseRejectionWarning: Error: timeout
    at Promise (...\scratch_2.es6:6:30)
    at new Promise (<anonymous>)
    at someHandler (...\scratch_2.es6:5:29)
    at Writable.write [as _write] (...\scratch_2.es6:29:4)
    at doWrite (_stream_writable.js:397:12)
    at writeOrBuffer (_stream_writable.js:383:5)
    at Writable.write (_stream_writable.js:290:11)
    at Object.<anonymous> (...\scratch_2.es6:36:9)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
(node:3288) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:3288) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

如果我省略了尝试写入块的最后一行代码,则第二个堆栈跟踪以及 UnhandledPromiseRejectionWarning-stuff 都消失了。

【讨论】:

  • 或许,不要用错误参数调用writedone函数?
  • @Bergi 把它放在一个答案中,我会让它被接受。
【解决方案2】:

坦率地说,您的代码非常混乱。在尝试调试上述问题之前,您最好像这样重写代码。

_write( data, encoding, doneFn ) {
    Promise.race( [
        this.getSocket(),
        /* This below code should better be handled within method this.getSocket so we won't need to use Promise.race */
        new Promise((resolve, reject) => {
            setTimeout( reject, 1000, Object.assign( new Error( `timeout on trying to connect w/ ${this.address}` ), { code: "ECONNREFUSED" } ) );
        }),
    ] )
        .then( socket => {
            return socket && new Promise( ( resolve, reject ) => {
                socket.once( "error", reject );
                socket.write( data, encoding, () => {
                    socket.removeListener( "error", reject );

                    resolve();
                } );
            } );
        } )
        .then(doneFn)
        .catch(error => {
            const { address } = this;

            switch ( error.code ) {
                case "EPIPE" :
                case "ECONNRESET" :
                    Log( `lost connection w/ ${address.id}` );
                    break;

                case "ECONNREFUSED" :
                    Log( `failed to connect w/ ${address.id}` );
                    break;
            }
            doneFn( error );
        });
}

【讨论】:

  • 试过那个模式。但它并没有阻止警告。
  • @cepharum 然后尝试通过检查方法 this.getSocket 中的 1000 毫秒超时条件来删除 Promise.race。但是这段代码仍然应该工作。确保错误不是来自其他地方。
  • 这不是一个选项,因为改变了那里有意要求的语义。写入的数据包应在超时后丢弃,但不应同时停止尝试建立连接。并且连续写入应该使用由 getSocket() 提供的相同承诺,承诺最终建立与对等点的连接。通过将超时检测移到那里,这种情况将不再可能。最终,Promise.race() 的存在是有原因的……
猜你喜欢
  • 2019-07-09
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-22
  • 1970-01-01
  • 2014-01-27
相关资源
最近更新 更多