Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancel a vanilla ECMAScript 6 Promise chain

People also ask

Can you cancel promise JavaScript?

To force cancel a promise with JavaScript, we use the AbortController constructor. const controller = new AbortController(); const task = new Promise((resolve, reject) => { //... controller.

What happens if you don't resolve a promise JavaScript?

A promise is just an object with properties in Javascript. There's no magic to it. So failing to resolve or reject a promise just fails to ever change the state from "pending" to anything else. This doesn't cause any fundamental problem in Javascript because a promise is just a regular Javascript object.

How do you pause a promise?

To pause the current promise, or to have it wait for the completion of another promise, simply return another promise from within then() . In the previous code sample, the alert will not be shown until the new promise has been resolved.


Is there a method for clearing the .thens of a JavaScript Promise instance?

No. Not in ECMAScript 6 at least. Promises (and their then handlers) are uncancellable by default (unfortunately). There is a bit of discussion on es-discuss (e.g. here) about how to do this in the right way, but whatever approach will win it won't land in ES6.

The current standpoint is that subclassing will allow to create cancellable promises using your own implementation (not sure how well that'll work).

Until the language commitee has figured out the best way (ES7 hopefully?) you can still use userland Promise implementations, many of which feature cancellation.

Current discussion is in the https://github.com/domenic/cancelable-promise and https://github.com/bergus/promise-cancellation drafts.


While there isn't a standard way of doing this in ES6, there is a library called Bluebird to handle this.

There is also a recommended way described as part of the react documentation. It looks similar to what you have in your 2 and 3rd updates.

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

const cancelablePromise = makeCancelable(
  new Promise(r => component.setState({...}}))
);

cancelablePromise
  .promise
  .then(() => console.log('resolved'))
  .catch((reason) => console.log('isCanceled', reason.isCanceled));

cancelablePromise.cancel(); // Cancel the promise

Taken from: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html


I'm really surprised that no-one mentions Promise.race as a candidate for this:

const actualPromise = new Promise((resolve, reject) => { setTimeout(resolve, 10000) });
let cancel;
const cancelPromise = new Promise((resolve, reject) => {
    cancel = reject.bind(null, { canceled: true })
})

const cancelablePromise = Object.assign(Promise.race([actualPromise, cancelPromise]), { cancel });

const makeCancelable = promise => {
    let rejectFn;

    const wrappedPromise = new Promise((resolve, reject) => {
        rejectFn = reject;

        Promise.resolve(promise)
            .then(resolve)
            .catch(reject);
    });

    wrappedPromise.cancel = () => {
        rejectFn({ canceled: true });
    };

    return wrappedPromise;
};

Usage:

const cancelablePromise = makeCancelable(myPromise);
// ...
cancelablePromise.cancel();

Promise can be cancelled with the help of AbortController.

Is there a method for clearing then: yes you can reject the promise with AbortController object and then the promise will bypass all then blocks and go directly to the catch block.

Example:

import "abortcontroller-polyfill";

let controller = new window.AbortController();
let signal = controller.signal;
let elem = document.querySelector("#status")

let example = (signal) => {
    return new Promise((resolve, reject) => {
        let timeout = setTimeout(() => {
            elem.textContent = "Promise resolved";
            resolve("resolved")
        }, 2000);

        signal.addEventListener('abort', () => {
            elem.textContent = "Promise rejected";
            clearInterval(timeout);
            reject("Promise aborted")
        });
    });
}

function cancelPromise() {
    controller.abort()
    console.log(controller);
}

example(signal)
    .then(data => {
        console.log(data);
    })
    .catch(error => {
        console.log("Catch: ", error)
    });

document.getElementById('abort-btn').addEventListener('click', cancelPromise);

Html


    <button type="button" id="abort-btn" onclick="abort()">Abort</button>
    <div id="status"> </div>

Note: need to add polyfill, not supported in all browser.

Live Example

Edit elegant-lake-5jnh3