Closed iarna closed 10 years ago
It may also be helpful to note that if I change:
Promise.fulfilled(23).then(function(V){ console.log("#",V) });
To:
Promise.fulfilled(23);
Then it goes back to working. Something about adding the chaining makes it fail to maintain the error domain.
Not saying anything about the bug but...
May I ask why you're using domains there? Promises have a strong throw safety guarantee.
I discovered this while writing a test for another module. I think you'll find that while error domains have limited general usefulness, they're often required in test suites when one is testing normally fatal scenarios.
In testing scenarios you can use the good old process.onUncaughtException
. Using unstable modules like domains to test your code is not a good idea:
Calling require("domain")
switches out the implementation of process.nextTick
, so if you have already called .then
etc somewhere which caused a nextTick
call, then something like this will happen.
The fix in your code is to call require("domain")
at the top:
"use strict";
var Promise = require('bluebird');
var domain = require("domain");
Promise.onPossiblyUnhandledRejection(function(error,promise) { throw error });
Promise.fulfilled(23).then(function(V){ console.log("#",V) });
domain.create()
.on('error', function(E){ console.log("ok trapped error "+E.message) })
.run(function() {
var P = new Promise(function(resolve,reject){ reject(new Error('boom')) });
});
A warning should probably be added somewhere about using domains (or better yet avoiding it) :)
On Tue, Mar 18, 2014 at 10:08 AM, Petka Antonov notifications@github.comwrote:
In testing scenarios you can use the good old process.onUncaughtException. Using unstable modules like domains to test your code is not a good idea:
Calling require("domain") switches out the implementation of process.nextTick, so if you have already called .then etc somewhere which caused a nextTick call, then something like this will happen.
The fix in your code is to call require("domain") at the top:
"use strict";var Promise = require('bluebird');var domain = require("domain"); Promise.onPossiblyUnhandledRejection(function(error,promise) { throw error }); Promise.fulfilled(23).then(function(V){ console.log("#",V) }); domain.create() .on('error', function(E){ console.log("ok trapped error "+E.message) }) .run(function() { var P = new Promise(function(resolve,reject){ reject(new Error('boom')) }); });
Reply to this email directly or view it on GitHubhttps://github.com/petkaantonov/bluebird/issues/148#issuecomment-37907825 .
WTF guys. I'm not asking for "help" with my code. I don't really care about that. I'm just reporting a bug in a feature that you currently have gone to some effort to support. If you don't intend to support domains then just document that.
I'm well aware that process.nextTick is swapped out. cough I'm the one that told YOU ALL that, a whole week ago when you'd accepted a massively more complicated patch to support domains.
I still stand by the idea that this is an error. I didn't throw in the first then block, so the fact that domains weren't loaded shouldn't enter into it. It's only because you're doing hinky things with nextTick queueing in schedule.js (edit: async.js) in the name of optimization that you're being bitten by this.
@iarna I don't understand your last comment at all. No one closed the issue or said it's not going to get fixed.
I was just suggesting adding a warning about using domains with promises in general, and Petka suggested a general workaround for testing.
In the name of optimization as someone who doesn't use domains I don't want to take any performance hit for domains. As someone who uses domains - you'd like to be able to include domains after you include the library. I don't think Petka is convinced that there is no alternative but sacrifice performance or working with domains and being Petka he is usually quiet about resolving it when he hasn't made up his mind or figured something out yet.
Also, to be fair, I think the ugly hack here is on Node's side by doing this swapping out but this is besides the point.
@benjamingr "I don't understand your last comment at all. "
Let me rephrase then:
When people offer "work arounds" or suggest that documenting arbitrary limitations that are likely to bite real world users, this sounds to me as a prelude to saying "works as documented" rather then actually either fixing the code or declaring the feature entirely unavailable.
What I'm advocating is either: 1) Documenting the library as not supporting domains or 2) fixing the bug. I do not have a preference for either. Please stop assuming that I do.
@benjamingr "As someone who uses domains - you'd like to be able to include domains after you include the library."
No, really, see, that's where you go wrong. I'm not someone who actually uses bluebird with domains. I'm just someone who found the bug. The test suite I found the problem in, was that of another promise library that I was running bluebird through as a curiosity.
And to be fair: I totally agree that Node's swapping that out is heinous. And it turns out that 0.11's "fix" for that solves the problem with folks who take a copy of process.nextTick, however this issue is not fixed by that.
I think I have a solution for this bug, but it requires a bit more testing. And yes, I'll compare benchmarks between versions too, to ensure that it does not measurably impact the speed of the library. The speed is absolutely the draw of it– it's astonishingly fast.
Fair enough, that clarified it.
Fwiw documenting the behavior as good or working around it was not suggested as a fix - I suggested a general warning about domains ( like a "do I need domains with promises?" section). I'll try to be clearer suggesting docs in the future :)
promises don't catch this TMK
aPromise().then(function(){
setImmediate(function(){
throw new Error("Won't get caught by promise");
});
}).catch(function(err){
// no
});
that's why we need domains, or domain-like functionality
@ORESoftware that's because you've used setImmediate
. In order to enjoy the throw-safety of promises you need to promisify all your APIs. If that setImmediate
becomes a Promise.delay
you can throw in it and things will work like you expect.
Also see https://github.com/petkaantonov/bluebird/issues/51
Much like anyone can call process.abort
in their code even if you use domains - there are always assumptions about how the code is used.
I hear from @benjamingr that process.on('unhandledRejection') can take care of this? or no?
thanks for response - the reason why I can't "promisify all my APIs" is because I am trying to write a test framework and I can make no assumptions about the code that the users of my library will throw into the tests, unfortunately..I do look forward to the days of try/catch around async/await, but those aren't quite here yet
@ORESoftware I am @benjamingr :)
unhandledRejection
would catch cases where a Promise rejected and no one handled it in a reasonable amount of time. If you throw inside a setImmediate
all bets are off - it creates really confusing cases and it's part of why domains are deprecated.
aPromise().then(function(){
return Promise.delay(0).then(function(){
throw new Error("Won't get caught by promise");
});
}).catch(function(err){
// yes
});
hmmmm, but Promises don't allow us to create (pseudo) async code...setTimeout, setImmediate, process.nextTick are what we need to do that, promises don't offer a substitute for those..so? We need process.nextTick and setImmediate, and we damn well may need them inside a promise chain.
also, as I mentioned, I am writing a test library, and I can't control the input (users will write code that may have setImmedate inside a promise etc). I believe you are very smart and I wish you would quit knocking domains until someone finds a reasonable alternative ;)
ok just kidding, perhaps promise.delay does act as a substitute, I just don't know enough about promises then...
you can wrap setimmediate as promise.immediate same way as delay wraps settimeout
@ORESoftware they most certainly do.
nextTick
Promise.resolve().then(() => {
// this code always runs in the nextTick, promises guard against Zalgo by
// always running the code when only platform code remained
});
setTimeout
Promise.delay(100).then(() => {
// after 100ms, uses timer internally
});
i see, hummity hum hums
what is Zalgo btw? Is that a real thing
Gotcha thanks, even after reading Isaac's post I am still not sure what Zalgo is but sounds a bit like Pandora's Boxxx On Feb 18, 2016 12:17 PM, "Benjamin Gruenbaum" notifications@github.com wrote:
@ORESoftware https://github.com/ORESoftware http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony
— Reply to this email directly or view it on GitHub https://github.com/petkaantonov/bluebird/issues/148#issuecomment-185896990 .
Specifically, the following works:
The above outputs the following, as expected:
However, if I rearrange the promises it fails:
And outputs:
Relates to #124