{"action":"created","issue":{"url":"https://api.github.com/repos/petkaantonov/bluebird/issues/415","labels_url":"https://api.github.com/repos/petkaantonov/bluebird/issues/415/labels{/name}","comments_url":"https://api.github.com/repos/petkaantonov/bluebird/issues/415/comments","events_url":"https://api.github.com/repos/petkaantonov/bluebird/issues/415/events","html_url":"https://github.com/petkaantonov/bluebird/issues/415","id":52971426,"number":415,"title":"3.0 cancellation overhaul","user":{"login":"petkaantonov","id":829734,"avatar_url":"https://avatars.githubusercontent.com/u/829734?v=3","gravatar_id":"","url":"https://api.github.com/users/petkaantonov","html_url":"https://github.com/petkaantonov","followers_url":"https://api.github.com/users/petkaantonov/followers","following_url":"https://api.github.com/users/petkaantonov/following{/other_user}","gists_url":"https://api.github.com/users/petkaantonov/gists{/gist_id}","starred_url":"https://api.github.com/users/petkaantonov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petkaantonov/subscriptions","organizations_url":"https://api.github.com/users/petkaantonov/orgs","repos_url":"https://api.github.com/users/petkaantonov/repos","events_url":"https://api.github.com/users/petkaantonov/events{/privacy}","received_events_url":"https://api.github.com/users/petkaantonov/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/petkaantonov/bluebird/labels/3.0.0","name":"3.0.0","color":"5319e7"}],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":98,"created_at":"2014-12-28T03:01:05Z","updated_at":"2015-10-27T14:36:30Z","closed_at":"2015-10-27T14:36:30Z","body":"The current cancellation seems to be the most problematic feature due to some annoying limits in the design:\r\n\r\n - Awkward API for attaching the callback that cancels the work (through typed catch handler)\r\n - Cannot compose because it is impossible to tell the difference between rejection and cancellation (e.g. `Promise.all(...).cancel()` cannot do the obvious thing and also call `cancel()` on the promises in the array)\r\n - `.cancel()` is asynchronous\r\n\r\nSince all consumers and producers must be trusted/\"your code\", there is no reason to enforce that cancellable promises are single-consumer or create new primitives.\r\n\r\n__Edit__: The below design has been scratched, see https://github.com/petkaantonov/bluebird/issues/415#issuecomment-73242358\r\n\r\n<del>\r\nIn the new cancellation you would register the callback while marking the promise cancellable:\r\n\r\n```js\r\npromise.cancellable(function onCancel(reason) {\r\n\r\n});\r\n```\r\n\r\nThis returns a new cancellable promise (unlike right now which just mutates existing promise which causes a lot of headache internally) and the flag will automatically propagate to all derived promises. However, the reference to the `onCancel` callback will only be held by the new promise created by `.cancellable()`.\r\nCalling `.cancel()` on a cancellable promise will keep propagating to cancellable parents and calling their `onCancel` callbacks.\r\n\r\nThe `onCancel` callback will receive the cancellation reason as its only argument (the default is a `CancellationError` instance).\r\n\r\nFrom the callback it's possible to decide the fate of the promise:\r\n\r\n - Throwing an error will reject the promise with the error as the rejection reason\r\n - Returning a value will fulfill the promise with the value as the fulfillment value\r\n\r\n(However I guess 99.99% of the time you want `throw reason`, so this flexibility adds a lot of inconvenience?)\r\n\r\nBad (should use .timeout() for this) example of usage:\r\n\r\n```js\r\nfunction delay(value, time) {\r\n    var timerId;\r\n    return new Promise(function(resolve, reject) {\r\n        timerId = setTimeout(function() {\r\n            resolve(value);\r\n        }, time || value);\r\n    }).cancellable(function(reason) {\r\n        clearTimeout(timerId);\r\n        throw reason;\r\n    });\r\n}\r\n\r\n\r\nvar after500ms = delay(500).catch(CancellationError, function(e) {\r\n    console.log(\"Your request took too long\");\r\n});\r\ndelay(250).then(function() {\r\n    after500ms.cancel();\r\n});\r\n```\r\n</del>"},"comment":{"url":"https://api.github.com/repos/petkaantonov/bluebird/issues/comments/151519978","html_url":"https://github.com/petkaantonov/bluebird/issues/415#issuecomment-151519978","issue_url":"https://api.github.com/repos/petkaantonov/bluebird/issues/415","id":151519978,"user":{"login":"petkaantonov","id":829734,"avatar_url":"https://avatars.githubusercontent.com/u/829734?v=3","gravatar_id":"","url":"https://api.github.com/users/petkaantonov","html_url":"https://github.com/petkaantonov","followers_url":"https://api.github.com/users/petkaantonov/followers","following_url":"https://api.github.com/users/petkaantonov/following{/other_user}","gists_url":"https://api.github.com/users/petkaantonov/gists{/gist_id}","starred_url":"https://api.github.com/users/petkaantonov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petkaantonov/subscriptions","organizations_url":"https://api.github.com/users/petkaantonov/orgs","repos_url":"https://api.github.com/users/petkaantonov/repos","events_url":"https://api.github.com/users/petkaantonov/events{/privacy}","received_events_url":"https://api.github.com/users/petkaantonov/received_events","type":"User","site_admin":false},"created_at":"2015-10-27T14:36:30Z","updated_at":"2015-10-27T14:36:30Z","body":"Fixed in 3.0 release"}}