Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Possibly unhandled Error" when settling synchronously in .any #432

Closed
joepie91 opened this issue Jan 10, 2015 · 2 comments
Closed

"Possibly unhandled Error" when settling synchronously in .any #432

joepie91 opened this issue Jan 10, 2015 · 2 comments

Comments

@joepie91
Copy link

While writing a module for determining the length of a stream (this is sometimes determined synchronously and sometimes determined asynchronously, depending on the type of stream), I ran into some strange "Possibly unhandled Error" stacktraces being printed to my console.

I eventually managed to write a reproducible minimal testcase (it's CoffeeScript, apologies, but the process should be clear nevertheless). In the process of writing this testcase, I also found that that stacktrace differed considerably depending on the exact approach used, but I'm not sure whether this is relevant.

Version: 2.6.2, from npm

The summary of the issue is as follows: When any of the Promises in a .any call are settled synchronously (whether resolved or rejected), a "Possibly unhandled Error" will be printed to the console after the .any aggregrate Promise has already resolved itself.

Expected behaviour: The Error should be silently ignored, as the .any Promise has already successfully resolved, and the use of .any indicates that we don't care about the rejected Promises, as long as at least one has resolved.

Promise = require "bluebird"

successPromise = (val) ->
    new Promise (resolve, reject) ->
        process.nextTick -> resolve(val)

failurePromise = (val) ->
    new Promise (resolve, reject) ->
        process.nextTick -> reject(val)


successSyncPromise = (val) ->
    new Promise (resolve, reject) ->
        resolve(val)

failureSyncPromise = (val) ->
    new Promise (resolve, reject) ->
        reject(val)

failureSyncPromiseTwo = (val) ->
    Promise.reject(val)


Promise.any [
    successSyncPromise()
    successPromise()
    failureSyncPromise("fail a")
]
.then -> console.log "success a"

Promise.any [
    successSyncPromise()
    successPromise()
    failurePromise("fail b")
]
.then -> console.log "success b"

Promise.any [
    successPromise()
    successPromise()
    failurePromise("fail c")
]
.then -> console.log "success c"

Promise.any [
    successSyncPromise()
    successSyncPromise()
    failureSyncPromise("fail d")
]
.then -> console.log "success d"

Promise.any [
    successSyncPromise()
    successSyncPromise()
    failureSyncPromiseTwo("fail e")
]
.then -> console.log "success e"

Output:

success a
success b
success d
success e
Possibly unhandled Error: fail a
  at /home/sven/projects/node-stream-length/node_modules/bluebird/js/main/promise.js:568:64
  at /home/sven/projects/node-stream-length/test-any.coffee:18:3
  at tryCatch2 (/home/sven/projects/node-stream-length/node_modules/bluebird/js/main/util.js:29:21)
  at Promise._resolveFromResolver (/home/sven/projects/node-stream-length/node_modules/bluebird/js/main/promise.js:562:13)
  at new Promise (/home/sven/projects/node-stream-length/node_modules/bluebird/js/main/promise.js:58:37)
  at failureSyncPromise (/home/sven/projects/node-stream-length/test-any.coffee:17:6)
  at Object.<anonymous> (/home/sven/projects/node-stream-length/test-any.coffee:27:2)
  at Object.<anonymous> (/home/sven/projects/node-stream-length/test-any.coffee:1:1)
  at Module._compile (module.js:456:26)

Possibly unhandled Error: fail d
  at /home/sven/projects/node-stream-length/node_modules/bluebird/js/main/promise.js:568:64
  at /home/sven/projects/node-stream-length/test-any.coffee:18:3
  at tryCatch2 (/home/sven/projects/node-stream-length/node_modules/bluebird/js/main/util.js:29:21)
  at Promise._resolveFromResolver (/home/sven/projects/node-stream-length/node_modules/bluebird/js/main/promise.js:562:13)
  at new Promise (/home/sven/projects/node-stream-length/node_modules/bluebird/js/main/promise.js:58:37)
  at failureSyncPromise (/home/sven/projects/node-stream-length/test-any.coffee:17:6)
  at Object.<anonymous> (/home/sven/projects/node-stream-length/test-any.coffee:48:2)
  at Object.<anonymous> (/home/sven/projects/node-stream-length/test-any.coffee:1:1)
  at Module._compile (module.js:456:26)

Possibly unhandled Error: fail e
  at Function.Promise.reject.Promise.rejected (/home/sven/projects/node-stream-length/node_modules/bluebird/js/main/promise.js:259:21)
  at failureSyncPromiseTwo (/home/sven/projects/node-stream-length/test-any.coffee:21:10)
  at Object.<anonymous> (/home/sven/projects/node-stream-length/test-any.coffee:55:2)
  at Object.<anonymous> (/home/sven/projects/node-stream-length/test-any.coffee:1:1)
  at Module._compile (module.js:456:26)

success c
Possibly unhandled Error: fail b
  at /home/sven/projects/node-stream-length/node_modules/bluebird/js/main/promise.js:568:64
  at /home/sven/projects/node-stream-length/test-any.coffee:9:23
  at process._tickCallback (node.js:419:13)
  at Function.Module.runMain (module.js:499:11)
  at startup (node.js:119:16)
  at node.js:906:3


@benjamingr
Copy link
Collaborator

Well, you:

  • Created promises that are rejected (akin to failed IO operations)
  • Are not adding .catch handlers to them at any point or otherwise handling them.

A any handler is not an indication that a rejection is handled, I don't think that in the general case it is an indication that we do not care about failure - especially since we can then the individual promises in the future. For example:

var p1 = getApi1();
var p2 = getApi2();
any(p1, p2).then(function(){
   // change state to indicate networking works since one resolved
   // or perform reports otherwise
});
p1.then(p1DoneHandler);

If p2 resolved but p1 did not you'd get a silent failure here.

You can always attach an empty catch handler to a promise to indicate you are explicitly suppressing errors - I think the point in unhandled rejection tracking is to make unhandled errors opt-out and not opt-in in the special cases you don't want to see them.

@joepie91
Copy link
Author

The problem is that the way bluebird handles rejections, differs depending on whether the settling is synchronous or asynchronous. If everything is settled asynchronously, no 'unhandled Error' stacktraces occur, and it is presumably ignoring them (as I would personally expect from .any).

If the intention is to treat unhandled errors as unhandled even when .any doesn't imply handling, then that really just changes the bug description to "bluebird does not report unhandled asynchronous rejections in .any". Case c is an example of that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants