<div dir="ltr"><div>In general I thing it would be good to have something like [``java``(``Thread.interrupt()``)](<a href="https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#interrupt()">https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#interrupt()</a>), assuming that "Thread" here can be "async stacktrace of promises", and interrupt notification should be just forwarded to top entry ([promise-executor](<a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Parameters">https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Parameters</a>)) and handled there arbitrary. In meta code it can be approximately expressed like  ``promise.interrupt(interruption_config)`` ==> ``promise.topPromiseInAwaitChain().injectInterruptionNotification(interruption_config)``. So it should be up to [promise-executor](<a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Parameters">https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Parameters</a>) - whether it want to complete abnormally on interruption (either with success or with failure), or just ignore this signal and continue execution without any reaction. It just assumes that general ``.then(...)``-like promises or ``async (...)  => {...}``-like promises should propagate interruption-signal transparently upward over async-stacktrace, and interruption-signal would be handled only in promises with executors which aware of interruption capability. In code it may look like</div><div><br></div><div>```js</div><div>const waitAsync = (delay) => (new Promise(</div><div>  (resOk, resErr, interuptionSignalEmitter) => {</div><div>    const beforeComplete = () => {clearTimeout(tid);}</div><div>    const tid = setTimeout(</div><div>      () => {beforeComplete(); resOk();},</div><div>      delay</div><div>    );</div><div>    interuptionSignalEmitter.on(</div><div>      (interuptionConfig) => {</div><div>        beforeComplete();</div><div>        if (interuptionConfig && interuptionConfig.raiseError) {</div><div>          resErr(new Error("abnormal interuption"));</div><div>        } else {</div><div>          resOk();</div><div>        }</div><div>      }</div><div>    );</div><div>  }</div><div>));</div><div><br></div><div>// waitAsync(100).then(() => {console.log("wait ok")}).interrupt() - should complete successfully </div><div>//   ("wait ok" message should be printed) but without delay in 100ms</div><div>// waitAsync(100).then(() => {console.log("wait ok")}).interrupt({raiseError: true}) - should complete with failure</div><div>//   (NO "wait ok" message should be printed) and without delay in 100ms</div><div>```</div><div><br></div><div><br></div><div>So, in other words, I would rather say, that we lack something like event capturing pahase when we intent for abnormal-completion/cancellation. I mean, if we have for example some async-stacktrace and some particular entry in it in the middle (some running and not yet completed promise), then it looks very natural that we may wish to "send a signal/message" downward over async-stacktrace, it just can be made by throwing something in that entry (and that "thrown something" will be naturally propagated downward async-stacktrace/promises-chain). But in certain cases we may need to "send a signal/message" upward over async-stacktrace which should generally end up by handling in very top promise executor (and if that top-most promise in chain decide to complete execution abnormally, then all clean-up in promises-chain also happens "abnormally"), while if we just "unsubscribe" some middle entry form it's "natural parent" and "abnormally" assign some result to that entry, then ofcourse, all cleanup in "upper part" of async-stacktrace will happen later (which ofcourse also can be desired behavior in some cases).</div><div><br></div><div>Jan-Ivar Bruaroey wrote:</div><div>> Because it doesn't make fetch stop fetching, which is what people want </div><div>> as I understand it (to not have the fetch keep going even though they've stopped waiting for it).</div><div><br></div><div>Completely agree, but I would rather prefer</div><div><br></div><div>```js</div><div>fetch(someUrl, someConfig).interuptOn(interruptionToken)</div><div>```</div><div>vs </div><div>```js</div><div>fetch(someUrl, {...someConfig, cancel: interruptionToken)</div><div>```</div><div>in this case ``.interruptOn`` can be easily defined on top of ``.interrupt`` like ``promise.interruptOn(interruptionReasonToken)`` <==> ``interruptionReasonToken.then((reason) => {promise.interrupt(reason)}), promise``</div><div><br></div><div><br></div><div>Bergi wrote:</div><div>> Yes, that's what I mean. Sure, I could use `Promise.race` to get the </div><div>> cancellation even if the non-cancellable operation resumes, but that's </div><div>> quite ugly:</div><div>> </div><div>> Promise.race([</div><div>>      promise()</div><div>>      …chain…,</div><div>>      cancelToken</div><div>> ]).then(callback);</div><div>> </div><div>> especially when you'll need to nest that pattern. Instead, I'd just like </div><div>> to write</div><div>> </div><div>> promise()</div><div>> …chain…</div><div>> .then(callback, cancelToken)</div><div>> </div><div>> with the same behaviour.</div><div><br></div><div>Very reasonable. left-to-right ``.`` chaining is much more readable and concise then wrap-like expression chaining.</div><div>But I would rather put ``cancelToken`` somewhere else, not in ``.then`` call.</div><div>From my perspective it can look like</div><div><br></div><div>```js</div><div>Promise.race([</div><div>     promise()</div><div>     …chain…,</div><div>     cancelToken</div><div>]).then(callback);</div><div>```</div><div><==></div><div>```js</div><div>promise() …chain… cancellable().cancelOn(cancelToken).then(callback);</div><div>```</div><div>Where ``Promise::cancellable`` (``Promise.prototype.cancellable``) can return some sort of "wrapper" - ``CancellablePromise(targetPromise)``</div><div>which in turns can be implemented based on ``Promise.race([targetPromise, CancellablePromise::privateInternalCancelToken])`` and that ``privateInternalCancelToken`` can be fully controlled by ``CancellablePromise`` itself and completed(fulfilled/rejected) when ever it needed (whether by direct call to ``cancellablePromise.completeNow(...)`` or caused by completion of some promise passed to ``.cancelOn`` method)</div><div><br></div><div>And finally</div><div><br></div><div>Bergi wrote:</div><div>> I'd however love to be able to cancel specific chaining operations, </div><div>> i.e. `then` callbacks.</div><div><br></div><div>As for me, definitely such kind of possibility should be available. However we can not unsubscribe ``then``-callback same easily as regular event handler. Since at least [some sort of ``finally``-callbacks](<a href="https://www.npmjs.com/package/promise.prototype.finally">https://www.npmjs.com/package/promise.prototype.finally</a>) should always work on any item in remaining promises chain. So promise then-unsubscription is tightly bound to providing immediate completion result for that target promise (which would be used instead of callback invocation result). And still it can be accomplished by some kind of third-party solutions, (like in  snippets above) like ``promise.cancellable().then(() => {doSomth()}).completeImmediately({error:new Error("unsubscribed abnormally")})``. If ``.cancellable()`` returns some wrapped instance like ``CancellablePromise(targetPromise)`` then ofcourse ``CancellablePromise`` can re-implement ``.then``, ``.catch``, etc by wrapping passed functions and by wrapping ``targetPromise.then(...wrappedCallbacks)`` with appropriate ``Promise.race([...])`` like ``Promise.race([targetPromise.then(...wrappedCallbacks), internalCancellationToken])``, and then when ``.completeImmediately`` invoked coordinate appropriately wrapped callbacks and ``internalCancellationToken`` to prevent initial callbacks from being executed and to complete "overall resulting promise" by the means of ``internalCancellationToken``. </div><div>But. I think disregarding that all of this stuff can be (more or less) implemented in 3-rd parity libraries, it would be much better if it was supported natively.</div></div>