Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 10:39 AM, Anne van Kesteren wrote: > On Wed, Oct 8, 2014 at 7:03 PM, Tab Atkins Jr. wrote: >> You keep ignoring the past "turns out we like using async errors for >> 'soft failures' of this kind, and have done it lots of times, and >> nobody seems to complain" argument. > > A user saying no to notifications is not an error. You ask the user to > make a decision, the user decides. Either way is a success. An error > would be invoking the method in the wrong way. This is more of a coding pattern thing than an expectation thing. For example in gecko we often implement security checks by calling functions that throw exceptions if the security check failed. Mainly because this means less typing. So rather than writing: function implDOMThingy(x, y, z) { if (!callerCanAccess(x)) throw Error(...); if (!isSameOrigin(x,y)) throw Error(...); ... do stuff here ... } we write function implDOMThingy(x, y, z) { assertCanAccess(x); assertSameOrigin(x,y); ... do stuff here ... } So assertCanAccess/assertSameOrigin throws exceptions if a given security check doesn't pass. Not because the security function was called the wrong way or otherwise had an error, but because it makes callsites easier to write. However the experience that I personally have with this pattern is that it requires that the throwing function takes care of everything that needs to happen when an exception is thrown. For us that meant things like log errors to the developer console. So this is more appropriate in places when the throwing function provides a complete package of functionality. This also means that the functions are less reusable. Since they don't just do one thing. But it is quite awesome when you have such a resuable package that you can use in several callsites. Definitely makes the code less cluttered. But I think that's an indication that we should not do that here. I would for example expect that authors want to put up some UI to the user indicating that the reason some feature isn't working is because the user has denied access. / Jonas
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 9:16 AM, Anne van Kesteren wrote: > On Wed, Oct 8, 2014 at 6:07 PM, Domenic Denicola > wrote: >> What I find interesting here is the claim that people find try/catch >> annoying or distasteful. > > I don't think you should need try/catch for a common failure case. > That is all. So yes, agreed with Tobie et al. Another thing to keep in mind here is that it's pretty easy to convert between either of these behaviors. It's just a matter of either doing requestPermission().then((r) => { if (!r) throw Error(...) }) requestPermission().then(() => true, () => false) Or, using the await syntax async function() { if (!await requestPermission()) throw Error(...); } async function() { hasPermission = true; try { await requestPermission(); } catch { hasPermission = false; } } So I think a more important question here is what behavior would an author expect. I think one of the points Dominic tried to make earlier is that the name is important for setting that expectation and help authors understand how the function behaves. Sadly the name that we're stuck with doesn't really provide much guidance either way. But I think to some extent it's indicating that we're bikeshedding here. / Jonas
Re: [whatwg] Notifications: making requestPermission() return a promise
On 10/08/2014 08:58 PM, Tab Atkins Jr. wrote: On Wed, Oct 8, 2014 at 10:54 AM, Olli Pettay wrote: On 10/08/2014 08:03 PM, Tab Atkins Jr. wrote: On Wed, Oct 8, 2014 at 9:16 AM, Anne van Kesteren wrote: On Wed, Oct 8, 2014 at 6:07 PM, Domenic Denicola wrote: What I find interesting here is the claim that people find try/catch annoying or distasteful. I don't think you should need try/catch for a common failure case. That is all. So yes, agreed with Tobie et al. You keep arguing from the future "rejections become errors" position. You keep ignoring the past "turns out we like using async errors for 'soft failures' of this kind, and have done it lots of times, and nobody seems to complain" argument. Do you dislike img.onerror firing when the image doesn't load? (And same for all the other resource-loading elements.) Do you dislike geolocator.getCurrentPosition calling the failure callback when the user refuses permission? Do you dislike IDB firing error events on several types of failure, ranging from exceptional to mundane? If there are any of these you do *not* dislike, why? And why doesn't the logic from those apply to this situation? On Wed, Oct 8, 2014 at 9:18 AM, Domenic Denicola wrote: Ah, this is the crux of our minor-disagreement, I think. IMO using try/catch for a common failure case is fine, *as long as you want that failure to bubble up the call stack*. E.g., if you want to handle it at a higher level along with other failures, or if you want to ignore the possibility of failure except in how errors get sent to `window.onerror`. Now, I think we're likely in *agreement* that you don't actually want to do this for requestPermission---you should handle it as soon as possible. But our reasoning is different, and now I understand why. The problem here is that try/catch *is* distasteful, while promise rejection isn't, and it's all ergonomics. * try/catch produces rightward drift, promise reject handlers do not * errors bubble up indefinitely until they're caught, breaking all of your code. (off topic to Notifications) That is a main point of exceptions. If your code is horribly broken, like wrong params passed to some method, execution should stop asap. The fact that Promise returning functions don't throw immediately is a crazy setup since one may just ignore and not notice at all the rather bad error in the program logic. As was stated in a recent bug thread you were participating in, we got feedback from authors using IDB that they actually quite disliked having to handle *some* errors via try/catch and *other* errors via error callbacks, and would have preferred they all go through the error callback. One feedback to one API... that isn't really a strong argument. "Fatal" errors indicating major issues in the program logic should be exposed to the scripts asap, not hidden to Promises (and probably not handled at all) -Olli ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 10:54 AM, Olli Pettay wrote: > On 10/08/2014 08:03 PM, Tab Atkins Jr. wrote: >> >> On Wed, Oct 8, 2014 at 9:16 AM, Anne van Kesteren >> wrote: >>> >>> On Wed, Oct 8, 2014 at 6:07 PM, Domenic Denicola >>> wrote: What I find interesting here is the claim that people find try/catch annoying or distasteful. >>> >>> >>> I don't think you should need try/catch for a common failure case. >>> That is all. So yes, agreed with Tobie et al. >> >> >> You keep arguing from the future "rejections become errors" position. >> You keep ignoring the past "turns out we like using async errors for >> 'soft failures' of this kind, and have done it lots of times, and >> nobody seems to complain" argument. >> >> Do you dislike img.onerror firing when the image doesn't load? (And >> same for all the other resource-loading elements.) Do you dislike >> geolocator.getCurrentPosition calling the failure callback when the >> user refuses permission? Do you dislike IDB firing error events on >> several types of failure, ranging from exceptional to mundane? >> >> If there are any of these you do *not* dislike, why? And why doesn't >> the logic from those apply to this situation? >> >> On Wed, Oct 8, 2014 at 9:18 AM, Domenic Denicola >> wrote: >>> >>> Ah, this is the crux of our minor-disagreement, I think. >>> >>> IMO using try/catch for a common failure case is fine, *as long as you >>> want that failure to bubble up the call stack*. E.g., if you want to handle >>> it at a higher level along with other failures, or if you want to ignore the >>> possibility of failure except in how errors get sent to `window.onerror`. >>> >>> Now, I think we're likely in *agreement* that you don't actually want to >>> do this for requestPermission---you should handle it as soon as possible. >>> But our reasoning is different, and now I understand why. >> >> >> The problem here is that try/catch *is* distasteful, while promise >> rejection isn't, and it's all ergonomics. >> >> * try/catch produces rightward drift, promise reject handlers do not >> * errors bubble up indefinitely until they're caught, breaking all of >> your code. > > (off topic to Notifications) > That is a main point of exceptions. If your code is horribly broken, > like wrong params passed to some method, execution should stop asap. > The fact that Promise returning functions don't throw immediately is a crazy > setup > since one may just ignore and not notice at all the rather bad error in the > program logic. As was stated in a recent bug thread you were participating in, we got feedback from authors using IDB that they actually quite disliked having to handle *some* errors via try/catch and *other* errors via error callbacks, and would have preferred they all go through the error callback. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On 10/08/2014 08:03 PM, Tab Atkins Jr. wrote: On Wed, Oct 8, 2014 at 9:16 AM, Anne van Kesteren wrote: On Wed, Oct 8, 2014 at 6:07 PM, Domenic Denicola wrote: What I find interesting here is the claim that people find try/catch annoying or distasteful. I don't think you should need try/catch for a common failure case. That is all. So yes, agreed with Tobie et al. You keep arguing from the future "rejections become errors" position. You keep ignoring the past "turns out we like using async errors for 'soft failures' of this kind, and have done it lots of times, and nobody seems to complain" argument. Do you dislike img.onerror firing when the image doesn't load? (And same for all the other resource-loading elements.) Do you dislike geolocator.getCurrentPosition calling the failure callback when the user refuses permission? Do you dislike IDB firing error events on several types of failure, ranging from exceptional to mundane? If there are any of these you do *not* dislike, why? And why doesn't the logic from those apply to this situation? On Wed, Oct 8, 2014 at 9:18 AM, Domenic Denicola wrote: Ah, this is the crux of our minor-disagreement, I think. IMO using try/catch for a common failure case is fine, *as long as you want that failure to bubble up the call stack*. E.g., if you want to handle it at a higher level along with other failures, or if you want to ignore the possibility of failure except in how errors get sent to `window.onerror`. Now, I think we're likely in *agreement* that you don't actually want to do this for requestPermission---you should handle it as soon as possible. But our reasoning is different, and now I understand why. The problem here is that try/catch *is* distasteful, while promise rejection isn't, and it's all ergonomics. * try/catch produces rightward drift, promise reject handlers do not * errors bubble up indefinitely until they're caught, breaking all of your code. (off topic to Notifications) That is a main point of exceptions. If your code is horribly broken, like wrong params passed to some method, execution should stop asap. The fact that Promise returning functions don't throw immediately is a crazy setup since one may just ignore and not notice at all the rather bad error in the program logic. Rejections bubble down a single promise chain, and can be ignored if you're done with a chain without anything bad happening. (And some Promise algebra functions like .race() can swallow a rejection without rejecting the chain, if another one fulfilled first, so you don't even have to think about the rejection.) * We've been taught not to use exceptions for control flow, but we have the Promise algebra to help us do control flow based on promises fulfilling/rejecting (and more mature Promise libraries tend to grow more algebra over time). Errors suck, but they're the way we do rejections syncly. I hate that we're compromising on the built-in ergonomics of Promises in order to avoid triggering the worse ergonomics of errors in the future. :( ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 10:39 AM, Anne van Kesteren wrote: > On Wed, Oct 8, 2014 at 7:03 PM, Tab Atkins Jr. wrote: >> You keep ignoring the past "turns out we like using async errors for >> 'soft failures' of this kind, and have done it lots of times, and >> nobody seems to complain" argument. > > A user saying no to notifications is not an error. You ask the user to > make a decision, the user decides. Either way is a success. An error > would be invoking the method in the wrong way. This is 100% arguable, and based solely on the *precise* manner in which you are assigning semantics to the name. If we named the method "getNotifier()", and it requested permission as a side-effect, would you call failure to get the notifier due to refusing permission an exceptional situation? >> Do you dislike img.onerror firing when the image doesn't load? (And >> same for all the other resource-loading elements.) > > That makes sense. Network errors are rather exceptional. Note that it > does not error for a 404 (unless it can't decode the response, which > again, is rather exceptional). > > >> Do you dislike >> geolocator.getCurrentPosition calling the failure callback when the >> user refuses permission? > > I would expect that to be done differently today, yes. Why? getCurrentPosition is asking for the current permission. If you fail to get the current position, why would you call that a success? What would you even pass to the success callback? A neutered position object? A position object with default values, plus an extra field saying "lol no this isn't real"? ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 7:03 PM, Tab Atkins Jr. wrote: > You keep ignoring the past "turns out we like using async errors for > 'soft failures' of this kind, and have done it lots of times, and > nobody seems to complain" argument. A user saying no to notifications is not an error. You ask the user to make a decision, the user decides. Either way is a success. An error would be invoking the method in the wrong way. > Do you dislike img.onerror firing when the image doesn't load? (And > same for all the other resource-loading elements.) That makes sense. Network errors are rather exceptional. Note that it does not error for a 404 (unless it can't decode the response, which again, is rather exceptional). > Do you dislike > geolocator.getCurrentPosition calling the failure callback when the > user refuses permission? I would expect that to be done differently today, yes. -- https://annevankesteren.nl/
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 9:16 AM, Anne van Kesteren wrote: > On Wed, Oct 8, 2014 at 6:07 PM, Domenic Denicola > wrote: >> What I find interesting here is the claim that people find try/catch >> annoying or distasteful. > > I don't think you should need try/catch for a common failure case. > That is all. So yes, agreed with Tobie et al. You keep arguing from the future "rejections become errors" position. You keep ignoring the past "turns out we like using async errors for 'soft failures' of this kind, and have done it lots of times, and nobody seems to complain" argument. Do you dislike img.onerror firing when the image doesn't load? (And same for all the other resource-loading elements.) Do you dislike geolocator.getCurrentPosition calling the failure callback when the user refuses permission? Do you dislike IDB firing error events on several types of failure, ranging from exceptional to mundane? If there are any of these you do *not* dislike, why? And why doesn't the logic from those apply to this situation? On Wed, Oct 8, 2014 at 9:18 AM, Domenic Denicola wrote: > Ah, this is the crux of our minor-disagreement, I think. > > IMO using try/catch for a common failure case is fine, *as long as you want > that failure to bubble up the call stack*. E.g., if you want to handle it at > a higher level along with other failures, or if you want to ignore the > possibility of failure except in how errors get sent to `window.onerror`. > > Now, I think we're likely in *agreement* that you don't actually want to do > this for requestPermission---you should handle it as soon as possible. But > our reasoning is different, and now I understand why. The problem here is that try/catch *is* distasteful, while promise rejection isn't, and it's all ergonomics. * try/catch produces rightward drift, promise reject handlers do not * errors bubble up indefinitely until they're caught, breaking all of your code. Rejections bubble down a single promise chain, and can be ignored if you're done with a chain without anything bad happening. (And some Promise algebra functions like .race() can swallow a rejection without rejecting the chain, if another one fulfilled first, so you don't even have to think about the rejection.) * We've been taught not to use exceptions for control flow, but we have the Promise algebra to help us do control flow based on promises fulfilling/rejecting (and more mature Promise libraries tend to grow more algebra over time). Errors suck, but they're the way we do rejections syncly. I hate that we're compromising on the built-in ergonomics of Promises in order to avoid triggering the worse ergonomics of errors in the future. :( ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
From: annevankeste...@gmail.com [mailto:annevankeste...@gmail.com] On Behalf Of Anne van Kesteren > I don't think you should need try/catch for a common failure case. Ah, this is the crux of our minor-disagreement, I think. IMO using try/catch for a common failure case is fine, *as long as you want that failure to bubble up the call stack*. E.g., if you want to handle it at a higher level along with other failures, or if you want to ignore the possibility of failure except in how errors get sent to `window.onerror`. Now, I think we're likely in *agreement* that you don't actually want to do this for requestPermission---you should handle it as soon as possible. But our reasoning is different, and now I understand why.
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 6:07 PM, Domenic Denicola wrote: > What I find interesting here is the claim that people find try/catch annoying > or distasteful. I don't think you should need try/catch for a common failure case. That is all. So yes, agreed with Tobie et al. -- https://annevankesteren.nl/
Re: [whatwg] Notifications: making requestPermission() return a promise
From: Tab Atkins Jr. [mailto:jackalm...@gmail.com] > Again, if this means that the current design for "await" becomes less > convenient, *we can fix await to work better*. It's not set in stone, it's > not developed yet. This is a thing we can change. To be clear, this will not be happening. Await is designed very much to play well with promise semantics and to carry over the return value/thrown exception parallel even more directly than promises already do. As I said earlier, if you'd prefer some kind of Option type, perhaps with its own dedicated syntax, that's something you can try proposing. But await and promises are tied to exception semantics, have been since day 1, and are in all other languages which have similar constructs. Nobody on TC39 is planning to depart from that path, especially since async functions with these semantics have been proposed, in one form or another, since the early days of ES Harmony. > But let's not twist our necks around and force promise APIs into an unnatural > and inconvenient shape just because a naive translation to sync code produces > errors and people find try/catch annoying or distasteful. I disagree with your characterization of "unnatural and inconvenient", of course. An `if` statement is the most natural thing in the world. What I find interesting here is the claim that people find try/catch annoying or distasteful. Why should they? It's the proper, syntactic way to handle errors---none of this "error" event business, or error-first callbacks, or even promise rejection callbacks. That's how exceptional, stack-unwinding errors that are meant to immediately halt execution should be contained and handled. The question, again, is very simple. Is being denied permission an exceptional error that developers want to bubble to the outermost `catch (e) { ... }` block, or outermost `.catch(e => { ... })` handler? Or will they always want to deal with both paths of the result immediately? Tobie's argument about treating permissions as progressive enhancement is a good argument for the latter, IMO.
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 5:59 PM, Tab Atkins Jr. wrote: > On Wed, Oct 8, 2014 at 1:31 AM, Tobie Langel > wrote: > > On Wed, Oct 8, 2014 at 9:51 AM, Tab Atkins Jr. > wrote: > >> > >> The question is whether it's not natural to assume that *if the promise > >> fulfills*, that means they got permission. This allows them to do things > >> like using Promise.all() to join multiple permission requests together > and > >> get a nice combined promise that fulfills when everything succeeds, > > > > > > This is as simple as: > > > > Promise.all(permissionRequests).then(function(results) { > > if (results.every(x => x === "granted")) // … > > }); > > > > But I don't think it's the right approach to handling permissions in > > general. Developers should handle granted permissions as progressive > > enhancements, not balk when they don't get all the permissions they > > required. Using exceptions for denied permissions sends a completely > wrong > > message imho, especially when it's combined with Promise.all. > > I don't think moral arguments really have a place here. Whatever > mitigation code that authors write to handle failed permissions can go > in the reject handler exactly as easily as in the fulfill handler. > That we believe authors should handle permission failures > intelligently doesn't, itself, mean that we should make the success > path less convenient. > I don't see how your definition of success is less of a moral argument than mine, but I'm happy to be enlightened. --tobie
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 1:31 AM, Tobie Langel wrote: > On Wed, Oct 8, 2014 at 9:51 AM, Tab Atkins Jr. wrote: >> >> The question is whether it's not natural to assume that *if the promise >> fulfills*, that means they got permission. This allows them to do things >> like using Promise.all() to join multiple permission requests together and >> get a nice combined promise that fulfills when everything succeeds, > > > This is as simple as: > > Promise.all(permissionRequests).then(function(results) { > if (results.every(x => x === "granted")) // … > }); > > But I don't think it's the right approach to handling permissions in > general. Developers should handle granted permissions as progressive > enhancements, not balk when they don't get all the permissions they > required. Using exceptions for denied permissions sends a completely wrong > message imho, especially when it's combined with Promise.all. I don't think moral arguments really have a place here. Whatever mitigation code that authors write to handle failed permissions can go in the reject handler exactly as easily as in the fulfill handler. That we believe authors should handle permission failures intelligently doesn't, itself, mean that we should make the success path less convenient. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 1:07 AM, Anne van Kesteren wrote: > On Wed, Oct 8, 2014 at 9:51 AM, Tab Atkins Jr. wrote: >> The question is whether it's not natural to assume that *if the promise >> fulfills*, that means they got permission. This allows them to do things >> like using Promise.all() to join multiple permission requests together and >> get a nice combined promise that fulfills when everything succeeds, or write >> a nice straight success path that assumes permission, and then handle any >> errors, including denied permission, at the end, rather than interleaving >> error-handling logic into the middle of your code. > > Okay, given that question I agree it's natural to assume that this > happens when it resolves. However, a user declining permission is not > exceptional behavior and therefore should not cause an exception. In > synchronous code you would not want to write try/catch here. An error or failure event *is* an asynchronous exception. It's a completely different code path from the success event that you can handle separately, while writing code for the success path that assumes you got the things you needed. Do you think that every single instance of error events we've used in the past was a mistake, and we should have instead just had a "complete" event that both success and failure used? For example, should .onload fire when the image fails to load? This is *exactly* analogous to a permission request succeeding or failing, as far as I can tell, but for the equivalent image situation, I believe you're okay with a failed load rejecting the .ready promise (or whatever we're planning to name it). There are certainly cases where only actual exceptions would cause rejection - Domenic's example of a "userAllowsNotifications()" function is clearly going to fulfill with a boolean, and only reject if you fuck something up. But requesting a permission is an operation that can fail, and failure is exceptional, and we've reflected this precise distinction in tons of past APIs via an error event. Why was every single past API that used this pattern mistaken? Again, if this means that the current design for "await" becomes less convenient, *we can fix await to work better*. It's not set in stone, it's not developed yet. This is a thing we can change. But let's not twist our necks around and force promise APIs into an unnatural and inconvenient shape just because a naive translation to sync code produces errors and people find try/catch annoying or distasteful. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 9:51 AM, Tab Atkins Jr. wrote: > The question is whether it's not natural to assume that *if the promise > fulfills*, that means they got permission. This allows them to do things > like using Promise.all() to join multiple permission requests together and > get a nice combined promise that fulfills when everything succeeds, This is as simple as: Promise.all(permissionRequests).then(function(results) { if (results.every(x => x === "granted")) // … }); But I don't think it's the right approach to handling permissions in general. Developers should handle granted permissions as progressive enhancements, not balk when they don't get all the permissions they required. Using exceptions for denied permissions sends a completely wrong message imho, especially when it's combined with Promise.all. --tobie
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 8, 2014 at 9:51 AM, Tab Atkins Jr. wrote: > The question is whether it's not natural to assume that *if the promise > fulfills*, that means they got permission. This allows them to do things > like using Promise.all() to join multiple permission requests together and > get a nice combined promise that fulfills when everything succeeds, or write > a nice straight success path that assumes permission, and then handle any > errors, including denied permission, at the end, rather than interleaving > error-handling logic into the middle of your code. Okay, given that question I agree it's natural to assume that this happens when it resolves. However, a user declining permission is not exceptional behavior and therefore should not cause an exception. In synchronous code you would not want to write try/catch here. -- https://annevankesteren.nl/
Re: [whatwg] Notifications: making requestPermission() return a promise
On Oct 7, 2014 11:32 PM, "Anne van Kesteren" wrote: > > On Tue, Oct 7, 2014 at 8:33 PM, Tab Atkins Jr. wrote: > > On Sun, Oct 5, 2014 at 7:41 AM, Anne van Kesteren wrote: > >> On Thu, Oct 2, 2014 at 10:13 PM, Domenic Denicola > >> wrote: > >>> So we should make a choice, as to whether we want developers to assume they will always get permission (in which case it should reject upon permission not being granted), or whether we want developers to ask the API whether they were granted permission (in which case it should give back a boolean fulfillment value or similar). > >> > >> How can they assume permission is always granted? It's up the user. > >> It's a request from the developer and the user can say no. What's > >> unclear about the name? > > > > Yeah, you misunderstood the question. It's about whether permission > > failure should reject the promise or accept it with a boolean. > > Domenic did not ask a question. I asked a question since it's unclear > to me why a developer could reasonably assume they will always get > permission. You're missing context or something. The question is whether it's not natural to assume that *if the promise fulfills*, that means they got permission. This allows them to do things like using Promise.all() to join multiple permission requests together and get a nice combined promise that fulfills when everything succeeds, or write a nice straight success path that assumes permission, and then handle any errors, including denied permission, at the end, rather than interleaving error-handling logic into the middle of your code. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Tue, Oct 7, 2014 at 8:33 PM, Tab Atkins Jr. wrote: > On Sun, Oct 5, 2014 at 7:41 AM, Anne van Kesteren wrote: >> On Thu, Oct 2, 2014 at 10:13 PM, Domenic Denicola >> wrote: >>> So we should make a choice, as to whether we want developers to assume they >>> will always get permission (in which case it should reject upon permission >>> not being granted), or whether we want developers to ask the API whether >>> they were granted permission (in which case it should give back a boolean >>> fulfillment value or similar). >> >> How can they assume permission is always granted? It's up the user. >> It's a request from the developer and the user can say no. What's >> unclear about the name? > > Yeah, you misunderstood the question. It's about whether permission > failure should reject the promise or accept it with a boolean. Domenic did not ask a question. I asked a question since it's unclear to me why a developer could reasonably assume they will always get permission. -- https://annevankesteren.nl/
Re: [whatwg] Notifications: making requestPermission() return a promise
On Sun, Oct 5, 2014 at 7:41 AM, Anne van Kesteren wrote: > On Thu, Oct 2, 2014 at 10:13 PM, Domenic Denicola > wrote: >> So we should make a choice, as to whether we want developers to assume they >> will always get permission (in which case it should reject upon permission >> not being granted), or whether we want developers to ask the API whether >> they were granted permission (in which case it should give back a boolean >> fulfillment value or similar). > > How can they assume permission is always granted? It's up the user. > It's a request from the developer and the user can say no. What's > unclear about the name? Yeah, you misunderstood the question. It's about whether permission failure should reject the promise or accept it with a boolean. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Oct 5, 2014 7:41 AM, "Anne van Kesteren" wrote: > > On Thu, Oct 2, 2014 at 10:13 PM, Domenic Denicola > wrote: > > So we should make a choice, as to whether we want developers to assume they will always get permission (in which case it should reject upon permission not being granted), or whether we want developers to ask the API whether they were granted permission (in which case it should give back a boolean fulfillment value or similar). > > How can they assume permission is always granted? It's up the user. > It's a request from the developer and the user can say no. What's > unclear about the name? > I think Domenic is saying "do we want to give the impression that you code the happy path only in the then(), or do we assume you are asking an async question for which an async answer is given with information for you to disambiguate in the then() regardless of whether this is happy or not. I originally expected denial to throw, I admit, but this is mainly because it was guessing on an unestablished pattern. If we establish something which can be applied widely, most of that is mitigated. I think the later (async question that always answers in .then()) makes much more sense especially given that https://notifications.spec.whatwg.org has 3 such values (accepted, denied, default) - if there is algebra to be done we can experiment with some good patterns inside .then() to make that easier. > > -- > https://annevankesteren.nl/
Re: [whatwg] Notifications: making requestPermission() return a promise
On Thu, Oct 2, 2014 at 10:13 PM, Domenic Denicola wrote: > So we should make a choice, as to whether we want developers to assume they > will always get permission (in which case it should reject upon permission > not being granted), or whether we want developers to ask the API whether they > were granted permission (in which case it should give back a boolean > fulfillment value or similar). How can they assume permission is always granted? It's up the user. It's a request from the developer and the user can say no. What's unclear about the name? -- https://annevankesteren.nl/
Re: [whatwg] Notifications: making requestPermission() return a promise
My previous replies to this thread have been about more general issues regarding promises and exceptions where I felt the need to jump in. Now, about the actual specific case at hand... From: whatwg [mailto:whatwg-boun...@lists.whatwg.org] On Behalf Of Anne van Kesteren > Otherwise I would never expect this promise to be rejected as the user > declining notifications is not exceptional. I think this is a judgment call. The definition of "exceptional" is always a bit tricky. Really it comes down to, "should the developer always handle this case or not?" For example, failures like a file being unreadable can be considered exceptional, since often a developer doesn't want to handle such failures, but instead wants to put a big try {} block around a bunch of logic, and if any of it failed, say "something went wrong". I also maintain that this is partially in how the function is named. For example, if your function is called `writeToFile()` or `acquireNotificationPermission()`, it can be considered exceptional for it to fail. To illustrate this, let's work in a world with only-sync calls, since that reduces peoples confusion about this new promises concept and what's appropriate there. The developer will likely include such calls in a sequence of operations, one after the other, and expect none of them to fail: ```js acquireNotificationPermission(); var data = prepareNotificationData(); showNotification(data); removeFromNotificationQueue(data); ``` in this example, any failures in any of these steps will bubble up to window.onerror, and get sent to telemetry. That seems OK, and in-line with the intent of the code. Whereas, if the function is named `userAllowsNotifications()`, it is much clearer that you would do ```js var canNotify = userAllowsNotifications(); if (canNotify) { var data = prepareNotificationData(); showNotification(data); removeFromNotificationQueue(data); } ``` So to me, the question comes down to: do we want to design our APIs around the model of `acquireNotificationPermission()`, where the developer codes as if getting permission is an expected normal thing? Or do we want to code them around the model of `userAllowsNotifications()`, where the developer is explicitly asking a question? To answer some contentions that the latter style would force duplicate error handling with promises: it would not. Very similar to sync code, you would write ```js userAllowsNotifications().then(canNotify => { if (canNotify) { var data = prepareNotificationData(); showNotification(data); removeFromNotificationQueue(data); } }); ``` and any other errors (e.g. programming errors, like if someone had typo'd prepareNotificationData()) would flow up to the developer tools, and eventually to window.onerror or similar once we get that specced [1]. You would not write an explicit `.catch(...)` to handle such failures. (And yes, only Firefox's developer tools correctly show such errors; Chrome is embarrassingly behind in that regard.) --- Bringing this back to requestPermission() and notifications: honestly, the name doesn't help me much in deciding which programming model we want. It could go either way. So we should make a choice, as to whether we want developers to assume they will always get permission (in which case it should reject upon permission not being granted), or whether we want developers to ask the API whether they were granted permission (in which case it should give back a boolean fulfillment value or similar). [1]: http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Sep/0024.html
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, 1 Oct 2014, Domenic Denicola wrote: > > > > This sort of behavior makes promise rejection essentially worthless. > > They are as "worthless" as exceptions. Some exceptions _are_ "worthless", as witnessed by the fact that nobody ever tries to catch them. For example, TypeError. This is why I've argued before that these worthless exceptions should continue to be real exceptions even in the Promise case. Incidentally, this bug and this comment in particular are quite relevant to this thread (regarding how to design an API with promises): https://www.w3.org/Bugs/Public/show_bug.cgi?id=25472#c18 -- Ian Hickson U+1047E)\._.,--,'``.fL http://ln.hixie.ch/ U+263A/, _.. \ _\ ;`._ ,. Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 11:43 PM, Anne van Kesteren wrote: > Given async/await the only reasonable thing to do seems to me > to model them after functions and only use rejection for something > exceptional, but with the level of disagreement this has created in > several different standards group thus far, I'm not hopeful initial > promise APIs will be consistent in this. > If that's not work for the TAG, then I don't know what their purpose is. Seriously, there needs to be agreement and consistency here, and unless there a design guidelines adopted across the board, this is just going to suck for Web developers. --tobie
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 8:30 PM, David Dorwin wrote: > I would specify that DOMException with the name "NotSupportedError" be > thrown. User agent implementations could provide more information in the > message. (There might be other "non-exceptional" failures that would use > different exception names.) We typically use non-throwing mechanism for unsupported features. E.g. properties that accept enumerations that when set to an unknown enumeration don't change. From experience, this tends to break pages less in user agents not offering certain features. This is also why XMLHttpRequest evolved to throw less exceptions over time. However, that seems like a bit of a digression from the main issue here. Which is whether promises-returning methods should be modeled after functions (return/throw) or have some kind of alternative design. Given async/await the only reasonable thing to do seems to me to model them after functions and only use rejection for something exceptional, but with the level of disagreement this has created in several different standards group thus far, I'm not hopeful initial promise APIs will be consistent in this. -- https://annevankesteren.nl/
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 11:02 AM, Boris Zbarsky wrote: > On 10/1/14, 1:59 PM, David Dorwin wrote: > >> Rejection also has the advantage of providing an exception, which can >> provide information (reason and message) to differentiate between >> potentially multiple causes. This is not possible when resolving with >> null. >> Providing such information would likely make development and debugging >> easier. >> > > If you were designing a sync API, would not the same arguments apply? So > you'd want to throw different kinds of exceptions to indicate "not > supported"? I would specify that DOMException with the name "NotSupportedError" be thrown. User agent implementations could provide more information in the message. (There might be other "non-exceptional" failures that would use different exception names.) However, it sounds like something not being supported by a UA would not be "exceptional," so an exception should not be thrown in this case. (This is no less expected than "the user declining notifications".) I guess the synchronous API would return null. That seems odd since an exception has historically been thrown in such cases. > > > -Boris > >
Re: [whatwg] Notifications: making requestPermission() return a promise
On 10/1/14, 1:59 PM, David Dorwin wrote: Rejection also has the advantage of providing an exception, which can provide information (reason and message) to differentiate between potentially multiple causes. This is not possible when resolving with null. Providing such information would likely make development and debugging easier. If you were designing a sync API, would not the same arguments apply? So you'd want to throw different kinds of exceptions to indicate "not supported"? -Boris
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 10:53 AM, Domenic Denicola < dome...@domenicdenicola.com> wrote: > > > > > > On Oct 1, 2014, at 18:22, Tab Atkins Jr. wrote: > > > >> On Wed, Oct 1, 2014 at 1:02 PM, Tobie Langel > wrote: > >>> On Wed, Oct 1, 2014 at 5:59 PM, Tab Atkins Jr. > wrote: > >>> I've never heard this opinion explicitly expressed, and it has never > >>> shown up in any API reviews of promise-using specs. It's directly > >>> contrary to the way that existing non-promise async APIs are > >>> constructed, and I expect quite contrary to most people's > >>> expectations. > >> > >> I'm with Domenic, here. We had these conversations for the Service > Worker > >> Cache's .match() method and dropped promise rejection in favor of > resolving > >> with null when a relevant Response object couldn't be found in the > cache. > >> Rejecting the promise was left for truly exceptional cases, such a data > >> corruption issues. > >> > >> I agree with you that the code branching provided by the resolve/reject > pair > >> looks appealing at first, but it's terrible once awaits comes in the > >> picture. > > > > Wow, that's kinda terrible. The operation literally failed; there is > > no way in which it could be said to have succeeded, and you absolutely > > want to run different code paths based on whether it succeeded or > > failed. Instead, you are forced to either run your failure-path code > > in the fulfill callback alongside the success-path code, or do what I > > said upthread and add a `if(arg == null) throw` line to the top of > > your fulfill callback so you can treat the fulfill callbacks as always > > succeeding. > > > > Note that Python, for example, throws errors on dict keys not being > > found (unless you specifically tell it a sentinel value to return > > instead). Do you think that's terrible? > > > > This sort of behavior makes promise rejection essentially worthless. > > They are as "worthless" as exceptions. > > > You can't base anything off of whether a promise fulfilled or not, > > because it'll only fail for weird exceptional cases; most of your > > "failures" (cases where the thing you were asking for couldn't be > > done) are instead mixed into your fulfill channel. > This is especially true if you wanted to try a series of calls (i.e. supported media features). If the resolve function is only called on success (i.e. supported and usable), then the rejection path can try the next prefered option. If valid-call-but-not-supported is reported to the resolve function and input-is-somehow-invalid-for-this-ua is reported as a rejection, an application needs to call the try next logic in both branches. Rejection also has the advantage of providing an exception, which can provide information (reason and message) to differentiate between potentially multiple causes. This is not possible when resolving with null. Providing such information would likely make development and debugging easier. > > > > ~TJ >
Re: [whatwg] Notifications: making requestPermission() return a promise
> On Oct 1, 2014, at 18:22, Tab Atkins Jr. wrote: > >> On Wed, Oct 1, 2014 at 1:02 PM, Tobie Langel wrote: >>> On Wed, Oct 1, 2014 at 5:59 PM, Tab Atkins Jr. wrote: >>> I've never heard this opinion explicitly expressed, and it has never >>> shown up in any API reviews of promise-using specs. It's directly >>> contrary to the way that existing non-promise async APIs are >>> constructed, and I expect quite contrary to most people's >>> expectations. >> >> I'm with Domenic, here. We had these conversations for the Service Worker >> Cache's .match() method and dropped promise rejection in favor of resolving >> with null when a relevant Response object couldn't be found in the cache. >> Rejecting the promise was left for truly exceptional cases, such a data >> corruption issues. >> >> I agree with you that the code branching provided by the resolve/reject pair >> looks appealing at first, but it's terrible once awaits comes in the >> picture. > > Wow, that's kinda terrible. The operation literally failed; there is > no way in which it could be said to have succeeded, and you absolutely > want to run different code paths based on whether it succeeded or > failed. Instead, you are forced to either run your failure-path code > in the fulfill callback alongside the success-path code, or do what I > said upthread and add a `if(arg == null) throw` line to the top of > your fulfill callback so you can treat the fulfill callbacks as always > succeeding. > > Note that Python, for example, throws errors on dict keys not being > found (unless you specifically tell it a sentinel value to return > instead). Do you think that's terrible? > > This sort of behavior makes promise rejection essentially worthless. They are as "worthless" as exceptions. > You can't base anything off of whether a promise fulfilled or not, > because it'll only fail for weird exceptional cases; most of your > "failures" (cases where the thing you were asking for couldn't be > done) are instead mixed into your fulfill channel. > > ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
> On Oct 1, 2014, at 16:59, Tab Atkins Jr. wrote: > > On Wed, Oct 1, 2014 at 11:44 AM, Domenic Denicola > wrote: >> From: whatwg [mailto:whatwg-boun...@lists.whatwg.org] On Behalf Of Tab >> Atkins Jr. >> >>> This is actually kinda terrible. Promises make it *really easy* to deal >>> with rejections *later*, letting you execute a bunch of code on the success >>> path and only at the end saying "Oh, did something along the line fail? Let >>> me take care of that.". Promise is basically an async Maybe monad, which >>> is great, because Maybe is useful for *exactly the scenario I just >>> outlined*. >> >> This is exactly the wrong way to think about promises. Promises are the >> "async return/throw monad"; if you want a Maybe, then you have to compose >> that in yourself. People often try to abuse the fact that they have a second >> branch for exceptions in order to use it as a generic container for "go down >> two possible flow paths", but that is very incorrect. An earlier comment in >> the thread about "we have three possible mechanisms" is also symptomatic of >> this incorrect thinking. We have two mechanisms: return and throw. When you >> wrap those in async, they become fulfill and reject. >> >>> I think we should develop Promises in a way that exploits their ergonomics >>> properly, and then rethink async/await a bit to make it match those >>> ergonomics, rather than fighting them. >> >> We are not changing the model of promises in this way. If you want a Maybe >> monad, that's a separate API that you'd want to spec. > > I've never heard this opinion explicitly expressed, http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/ http://www.w3.org/2001/tag/doc/promises-guide#rejections-should-be-exceptional > and it has never > shown up in any API reviews of promise-using specs. It's directly > contrary to the way that existing non-promise async APIs are > constructed, and I expect quite contrary to most people's > expectations. > > Geo, for example, throws if you pass bad arguments, but then routes > its result through either a success or error callback. This is > *directly* analogous to the signature of Promise.then(), and I think a > lot of people would expect a getCurrentPosition() that returned a > promise would have the same behavior. This allows you to just assume > that the value passed to the success callback is a position, and write > code accordingly. > > It feels like your position leads directly to the "error handling in > two places" problem that you, I, and others railed against in the > "promise-returning functions should never throw" debate. You have to > handle bad arguments in the reject callback to the promise, and handle > failure in the fulfill callback. It'll lead to a ton of boilerplate > `if(arg == null) throw;` at the top of fulfill callbacks, so that the > fulfill chain is *actually for the successful case*. > > ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 7:22 PM, Tab Atkins Jr. wrote: > Note that Python, for example, throws errors on dict keys not being > found (unless you specifically tell it a sentinel value to return > instead). Do you think that's terrible? Sure. But JS doesn't.
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 1:02 PM, Tobie Langel wrote: > On Wed, Oct 1, 2014 at 5:59 PM, Tab Atkins Jr. wrote: >> I've never heard this opinion explicitly expressed, and it has never >> shown up in any API reviews of promise-using specs. It's directly >> contrary to the way that existing non-promise async APIs are >> constructed, and I expect quite contrary to most people's >> expectations. > > I'm with Domenic, here. We had these conversations for the Service Worker > Cache's .match() method and dropped promise rejection in favor of resolving > with null when a relevant Response object couldn't be found in the cache. > Rejecting the promise was left for truly exceptional cases, such a data > corruption issues. > > I agree with you that the code branching provided by the resolve/reject pair > looks appealing at first, but it's terrible once awaits comes in the > picture. Wow, that's kinda terrible. The operation literally failed; there is no way in which it could be said to have succeeded, and you absolutely want to run different code paths based on whether it succeeded or failed. Instead, you are forced to either run your failure-path code in the fulfill callback alongside the success-path code, or do what I said upthread and add a `if(arg == null) throw` line to the top of your fulfill callback so you can treat the fulfill callbacks as always succeeding. Note that Python, for example, throws errors on dict keys not being found (unless you specifically tell it a sentinel value to return instead). Do you think that's terrible? This sort of behavior makes promise rejection essentially worthless. You can't base anything off of whether a promise fulfilled or not, because it'll only fail for weird exceptional cases; most of your "failures" (cases where the thing you were asking for couldn't be done) are instead mixed into your fulfill channel. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 5:59 PM, Tab Atkins Jr. wrote: > I've never heard this opinion explicitly expressed, and it has never > shown up in any API reviews of promise-using specs. It's directly > contrary to the way that existing non-promise async APIs are > constructed, and I expect quite contrary to most people's > expectations. > I'm with Domenic, here. We had these conversations for the Service Worker Cache's .match() method and dropped promise rejection in favor of resolving with null when a relevant Response object couldn't be found in the cache. Rejecting the promise was left for truly exceptional cases, such a data corruption issues. I agree with you that the code branching provided by the resolve/reject pair looks appealing at first, but it's terrible once awaits comes in the picture.
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 11:52 AM, Jeffrey Yasskin wrote: > On Wed, Oct 1, 2014 at 6:06 AM, Anne van Kesteren wrote: >> On Wed, Oct 1, 2014 at 2:56 PM, Peter Beverloo wrote: >>> One argument I came across for overloading requestPermission is the >>> following: >>> Promise.all([ Notification.requestPermission(), >>> swRegistration.push.requestPermission() ]).then(...); >>> >>> Might be worth considering, it's relatively cheap to support and can be >>> implemented without breaking backwards compatibility. >> >> One minor risk with also returning a promise is that exceptions for >> incorrect invocation would no longer throw an exception, but instead >> reject the promise. >> >> Otherwise I would never expect this promise to be rejected as the user >> declining notifications is not exceptional. > > On a distributed system, a network error isn't unusual either, but it > still makes sense to treat it as an exception because the > application's main codepath can't continue executing. Similarly, if a > permission the application expects isn't granted, the application has > to skip the rest of its main codepath, so it makes sense to treat that > as an exception too. Precisely. > If Tab wants to avoid try/catch blocks around most of his code, he can > simply avoid using await for those promises, and transform their > values with .catch(), but exceptions are really the same thing as > «deal with rejections *later*, letting you execute a bunch of code on > the success path and only at the end saying "Oh, did something along > the line fail? Let me take care of that.".» Yeah, you're right. try/catch is just more annoying to use than a .catch() at the end of a promise chain. ^_^ On Wed, Oct 1, 2014 at 11:34 AM, Tab Atkins Jr. wrote: > I think we should develop Promises in a way that exploits their > ergonomics properly, and then rethink async/await a bit to make it > match those ergonomics, rather than fighting them. For example, we could have *two* keywords for "awaiting" - one that throws all rejections, and one that converts "non-fatal" errors (tagged in the exception somehow) into fulfills for you automatically. Or a method on Promise.prototype that does the same. That way a permission rejection can reject a promise usefully (letting you count on the success path as meaning "I got permission"), and someone can still, if they wish, handle no-permission in their normal code path and leave errors for actual coding errors. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 11:44 AM, Domenic Denicola wrote: > From: whatwg [mailto:whatwg-boun...@lists.whatwg.org] On Behalf Of Tab Atkins > Jr. > >> This is actually kinda terrible. Promises make it *really easy* to deal >> with rejections *later*, letting you execute a bunch of code on the success >> path and only at the end saying "Oh, did something along the line fail? Let >> me take care of that.". Promise is basically an async Maybe monad, which is >> great, because Maybe is useful for *exactly the scenario I just outlined*. > > This is exactly the wrong way to think about promises. Promises are the > "async return/throw monad"; if you want a Maybe, then you have to compose > that in yourself. People often try to abuse the fact that they have a second > branch for exceptions in order to use it as a generic container for "go down > two possible flow paths", but that is very incorrect. An earlier comment in > the thread about "we have three possible mechanisms" is also symptomatic of > this incorrect thinking. We have two mechanisms: return and throw. When you > wrap those in async, they become fulfill and reject. > >> I think we should develop Promises in a way that exploits their ergonomics >> properly, and then rethink async/await a bit to make it match those >> ergonomics, rather than fighting them. > > We are not changing the model of promises in this way. If you want a Maybe > monad, that's a separate API that you'd want to spec. I've never heard this opinion explicitly expressed, and it has never shown up in any API reviews of promise-using specs. It's directly contrary to the way that existing non-promise async APIs are constructed, and I expect quite contrary to most people's expectations. Geo, for example, throws if you pass bad arguments, but then routes its result through either a success or error callback. This is *directly* analogous to the signature of Promise.then(), and I think a lot of people would expect a getCurrentPosition() that returned a promise would have the same behavior. This allows you to just assume that the value passed to the success callback is a position, and write code accordingly. It feels like your position leads directly to the "error handling in two places" problem that you, I, and others railed against in the "promise-returning functions should never throw" debate. You have to handle bad arguments in the reject callback to the promise, and handle failure in the fulfill callback. It'll lead to a ton of boilerplate `if(arg == null) throw;` at the top of fulfill callbacks, so that the fulfill chain is *actually for the successful case*. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 6:06 AM, Anne van Kesteren wrote: > On Wed, Oct 1, 2014 at 2:56 PM, Peter Beverloo wrote: >> One argument I came across for overloading requestPermission is the >> following: >> Promise.all([ Notification.requestPermission(), >> swRegistration.push.requestPermission() ]).then(...); >> >> Might be worth considering, it's relatively cheap to support and can be >> implemented without breaking backwards compatibility. > > One minor risk with also returning a promise is that exceptions for > incorrect invocation would no longer throw an exception, but instead > reject the promise. > > Otherwise I would never expect this promise to be rejected as the user > declining notifications is not exceptional. On a distributed system, a network error isn't unusual either, but it still makes sense to treat it as an exception because the application's main codepath can't continue executing. Similarly, if a permission the application expects isn't granted, the application has to skip the rest of its main codepath, so it makes sense to treat that as an exception too. If Tab wants to avoid try/catch blocks around most of his code, he can simply avoid using await for those promises, and transform their values with .catch(), but exceptions are really the same thing as «deal with rejections *later*, letting you execute a bunch of code on the success path and only at the end saying "Oh, did something along the line fail? Let me take care of that.".»
Re: [whatwg] Notifications: making requestPermission() return a promise
From: whatwg [mailto:whatwg-boun...@lists.whatwg.org] On Behalf Of Tab Atkins Jr. > This is actually kinda terrible. Promises make it *really easy* to deal with > rejections *later*, letting you execute a bunch of code on the success path > and only at the end saying "Oh, did something along the line fail? Let me > take care of that.". Promise is basically an async Maybe monad, which is > great, because Maybe is useful for *exactly the scenario I just outlined*. This is exactly the wrong way to think about promises. Promises are the "async return/throw monad"; if you want a Maybe, then you have to compose that in yourself. People often try to abuse the fact that they have a second branch for exceptions in order to use it as a generic container for "go down two possible flow paths", but that is very incorrect. An earlier comment in the thread about "we have three possible mechanisms" is also symptomatic of this incorrect thinking. We have two mechanisms: return and throw. When you wrap those in async, they become fulfill and reject. > I think we should develop Promises in a way that exploits their ergonomics > properly, and then rethink async/await a bit to make it match those > ergonomics, rather than fighting them. We are not changing the model of promises in this way. If you want a Maybe monad, that's a separate API that you'd want to spec.
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 9:34 AM, Anne van Kesteren wrote: > On Wed, Oct 1, 2014 at 3:21 PM, Tab Atkins Jr. wrote: >> And I wouldn't expect someone loading a FontFace synchronously to use >> try/catch to deal with loading errors, either, because that's super >> obnoxious. Failure, though, is a standard rejection reason - it maps >> to the use of "onerror" events. >> >> Without it, the promise algebra functions become far less useful, and >> you have to type-test the fulfillment value to see if it's actually >> the value you want, or some sort of proxy that communicates failure. > > Once we have async/await syntax the synchronous version is what you > get. I would not want try/catch for requestPermission() there. As far > as I know promises are just like functions in that regard, you only > want to reject/throw if you want to force try/catch on the user. Yeah, this is a mismatch of expectations based on how you use it. If you're using Promises, and the promise algebra like .all() or .race(), you expect only "success" to result in fulfillment; "failure" should reject, so you can specialize your code paths properly and have the algebra work well. I want to be able to use Promise.all() on several permission requests to tell if I get them all; if they always fulfill, Promise.all() becomes solely a synchronization primitive, not a useful value algebra operation. If you're using async/await to hide the asynchrony, you only want error situations to throw, because dealing with throwing is really annoying. This is actually kinda terrible. Promises make it *really easy* to deal with rejections *later*, letting you execute a bunch of code on the success path and only at the end saying "Oh, did something along the line fail? Let me take care of that.". Promise is basically an async Maybe monad, which is great, because Maybe is useful for *exactly the scenario I just outlined*. Changing things so that you have to go back to wrapping all your code in try/catch (and not just the one call - if you're chaining promises, they *all* have to be wrapped) is kinda terrible, and distorts the ergonomics of the system. What was once great, when done directly with Promises, is now terrible, when done with async/await; you're forced to pay for that convenience! I think we should develop Promises in a way that exploits their ergonomics properly, and then rethink async/await a bit to make it match those ergonomics, rather than fighting them. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 3:21 PM, Tab Atkins Jr. wrote: > And I wouldn't expect someone loading a FontFace synchronously to use > try/catch to deal with loading errors, either, because that's super > obnoxious. Failure, though, is a standard rejection reason - it maps > to the use of "onerror" events. > > Without it, the promise algebra functions become far less useful, and > you have to type-test the fulfillment value to see if it's actually > the value you want, or some sort of proxy that communicates failure. Once we have async/await syntax the synchronous version is what you get. I would not want try/catch for requestPermission() there. As far as I know promises are just like functions in that regard, you only want to reject/throw if you want to force try/catch on the user. -- https://annevankesteren.nl/
Re: [whatwg] Notifications: making requestPermission() return a promise
On 01/10/14 14:21, Tab Atkins Jr. wrote: > On Wed, Oct 1, 2014 at 9:18 AM, Anne van Kesteren wrote: >> On Wed, Oct 1, 2014 at 3:14 PM, Tab Atkins Jr. wrote: >>> Wait, what? Anytime you request something, not getting it is >>> exceptional. Not sure how you can make an argument otherwise. >> >> I would not expect a synchronous version of this method (were it to >> exist) to have to use try/catch for anything other than invoking it >> with an argument such as "TEST", which is clearly wrong. That's why I >> don't think it's exceptional (e.g. warrants an exception/rejection). > > And I wouldn't expect someone loading a FontFace synchronously to use > try/catch to deal with loading errors, either, because that's super > obnoxious. Failure, though, is a standard rejection reason - it maps > to the use of "onerror" events. Isn't this just a problem that we have three possible outcomes: * Permission grant * Permission reject * Invalid input data And three possible ways of routing the code: * Promise fulfilled * Promise rejected * Exception But we are only using two of them? In that case something has to give; you either need to disambiguate user grant vs user reject in the fulfill function or user reject vs invalid data in the rejection function. Neither seems obviously to have better ergonomics than the other.
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 9:18 AM, Anne van Kesteren wrote: > On Wed, Oct 1, 2014 at 3:14 PM, Tab Atkins Jr. wrote: >> Wait, what? Anytime you request something, not getting it is >> exceptional. Not sure how you can make an argument otherwise. > > I would not expect a synchronous version of this method (were it to > exist) to have to use try/catch for anything other than invoking it > with an argument such as "TEST", which is clearly wrong. That's why I > don't think it's exceptional (e.g. warrants an exception/rejection). And I wouldn't expect someone loading a FontFace synchronously to use try/catch to deal with loading errors, either, because that's super obnoxious. Failure, though, is a standard rejection reason - it maps to the use of "onerror" events. Without it, the promise algebra functions become far less useful, and you have to type-test the fulfillment value to see if it's actually the value you want, or some sort of proxy that communicates failure. ~TJ
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 3:14 PM, Tab Atkins Jr. wrote: > Wait, what? Anytime you request something, not getting it is > exceptional. Not sure how you can make an argument otherwise. I would not expect a synchronous version of this method (were it to exist) to have to use try/catch for anything other than invoking it with an argument such as "TEST", which is clearly wrong. That's why I don't think it's exceptional (e.g. warrants an exception/rejection). -- https://annevankesteren.nl/
Re: [whatwg] Notifications: making requestPermission() return a promise
On Wed, Oct 1, 2014 at 9:06 AM, Anne van Kesteren wrote: > On Wed, Oct 1, 2014 at 2:56 PM, Peter Beverloo wrote: >> One argument I came across for overloading requestPermission is the >> following: >> Promise.all([ Notification.requestPermission(), >> swRegistration.push.requestPermission() ]).then(...); >> >> Might be worth considering, it's relatively cheap to support and can be >> implemented without breaking backwards compatibility. > > One minor risk with also returning a promise is that exceptions for > incorrect invocation would no longer throw an exception, but instead > reject the promise. > > Otherwise I would never expect this promise to be rejected as the user > declining notifications is not exceptional. Wait, what? Anytime you request something, not getting it is exceptional. Not sure how you can make an argument otherwise. ~TJ