w3ctag / promises-guide

A guide for spec authors on how to use Promises in prose and WebIDL.
https://www.w3.org/2001/tag/doc/promises-guide
192 stars 24 forks source link

guidance on what error to specify is used when a promise is rejected #60

Open jpiesing opened 5 years ago

jpiesing commented 5 years ago

The document currently says;

4.1.2. Rejection Reasons Should Be Errors

Promise rejection reasons should always be instances of the ECMAScript Error type, just like synchronously-thrown exceptions should always be instances of Error as well.

In particular, for DOM or other web platform specs, this means you should never use DOMError, but instead use DOMException, which per WebIDL extends Error. You can of course also use one of the built-in ECMAScript error types.

Looking at existing web APIs, it seems that the most common error to use is one of the built-in ECMAScript error types (e.g. TypeError). I've seen a few examples of DOMException. I've not (yet) found any APIs which define their own Error classes to use with rejection.

When defining an API, sometimes the existing ECMAScript error types include a good fit ( AbortError, NotSupportedError, InvalidStateError). Sometimes they don't or there's a need to distinguish that a promise was rejected for a specific reason.

Please can be document be revised to give some more guidance on what to do when one of the built-in ECMAScript error types is not right.

domenic commented 5 years ago

Such situations are not specific to promises, so I'm not sure this document is the best place to give such guidance. They apply for synchronously-thrown exceptions as well.

In general we've found on the web platform that giving detailed error information is not very useful; authors rarely write code that distinguishes between different exceptional error cases. (Different non-exceptional error cases sometimes do get different responses from author code, but in those cases you shouldn't be throwing exceptions/rejecting promises anyway.)

jpiesing commented 5 years ago

Such situations are not specific to promises, so I'm not sure this document is the best place to give such guidance. They apply for synchronously-thrown exceptions as well.

Point taken.

In general we've found on the web platform that giving detailed error information is not very useful; authors rarely write code that distinguishes between different exceptional error cases. (Different non-exceptional error cases sometimes do get different responses from author code, but in those cases you shouldn't be throwing exceptions/rejecting promises anyway.)

Please forgive me if I'm being stupid but ... If an API uses promises for exceptional error cases, it would seem really strange to revert back to something else for non-exceptional error cases. This is particularly true for async failure modes after method has returned. As far as I can see, the alternative to using promises in these circumstances for a non-exceptional error case would be to use events. That feels really strange to me.

domenic commented 5 years ago

You can continue to use promises for asynchronous operations. You should just not signal non-exceptional errors using promise rejections. Instead use different fulfillment values. See https://www.w3.org/2001/tag/doc/promises-guide#rejections-should-be-exceptional

jpiesing commented 5 years ago

You can continue to use promises for asynchronous operations. You should just not signal exceptional errors using promise rejections. Instead use different fulfillment values. See https://www.w3.org/2001/tag/doc/promises-guide#rejections-should-be-exceptional

"Good cases for rejections include:" "..." "When it will be impossible to complete the requested task: "

There can be non-exceptional async error cases which mean it's impossible to complete the requested task. If I wasn't trying to use promises, this would be a somethingFailedEvent.

annevk commented 5 years ago

With promises you resolve with a different value for those cases. Similar to how a function might have different return values. (To be clear, in part this is a judgment call, but if the caller is expected to handle the error in a normal flow it's not a rejection.)

jpiesing commented 5 years ago

clear

Thanks for that. The use case I'm looking at is a failure of an operation for a transient reason which may not happen in a few minutes time. It's not an error by the caller. It's not something which will never work. The caller would however need to re-try.

annevk commented 5 years ago

If you'd model this as a function call (that happens to take a few minutes), would you return something like "retry" or would you throw? I'd expect the former to make more sense here. And hence, you wanna resolve with something like "retry".

jpiesing commented 5 years ago

If you'd model this as a function call (that happens to take a few minutes), would you return something like "retry" or would you throw? I'd expect the former to make more sense here. And hence, you wanna resolve with something like "retry".

If I modeled this as a function call that took several 10s of seconds then yes, the function would return "retry" rather than throw an Error / Exception.

annevk commented 5 years ago

There's your answer. Perhaps we should put that explicitly in the guide. That you should always think of them as regular function calls except that you resolve instead of return values/errors and reject rather than throw exceptions.

jpiesing commented 5 years ago

There's your answer. Perhaps we should put that explicitly in the guide. That you should always think of them as regular function calls except that you resolve instead of return values/errors and reject rather than throw exceptions.

That would make it clearer about how to handle error conditions that would not be serious enough to throw an error / exception in an API where promises were not being used..

jpiesing commented 5 years ago

Also please could you consider adding an example to the guide where a promise is resolved to something other than undefined but still simple - e.g. a constant String.

atanassov commented 4 years ago

@plinss and myself looked at this issue during May 2020 virtual f2f.

It seems the main question was already answered well by @annevk with this comment.

The next question about when to return with error and when to reject is already captured under Section 4.1.3..

We recommend adding a note before 4.1.3., something like: "If you would've return an error value in case this was a sync function, then resolve the promise with the error value. If you would normally throw an exception in the same API, the go ahead and Reject the promise instead with the same exception."