Possible to add a cancel method to Promise in Typescript?

Related searches

I'd like to create a QueryPromise that is just a concrete promise with a cancel method. Here's how it would be used:

function runQuery(text: string): QueryPromise {
  return new QueryPromise((resolve,reject) => {nativeQuery(resolve)})
}

Here is my first attempt, hopefully pretty understandable:

interface CancellablePromise<T> extends Promise<T> {
  cancel: () => void
}

// create the promise my app will use
type QueryPromise = CancellablePromise<string|boolean>

But that's not sufficient.

After a few hours of trial and error I managed to get close, but this seems tedious and anything but DRY.

interface CancellablePromise<T> extends Promise<T> {
  cancel: () => void
}
// also need this interface so the variable can be declared
// plus need to redeclare methods to return the new type
interface CancellablePromiseConstructor extends PromiseConstructor {
  new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): CancellablePromise<T>;
  cancel: () => void
}

type QueryPromise = CancellablePromise<string|boolean>    // for parameter types
var QueryPromise: CancellablePromiseConstructor = Promise // for the new operator

// some example code to exercise the promise
function runQuery(text: string): QueryPromise {
  return new QueryPromise((resolve,reject) => {nativeQuery(resolve)})
}

I feel like I've gone a long way down the wrong road... Does anyone know of a better way to do this?


Canceling Promises in JavaScript. Or: How to Cancel That , (You could also set a cancel method on your Promise before you return it, like promise.cancel = cancel; , but TypeScript/IDE IntelliSense might� As you can see, we changed signature – now we return object instead of a promise, but in fact we can just pass a parameter to the function, an object, and attach cancel method on it (or monkey-patch instance of Promise, but it can create you problems later). If you have this requirement only in couple places, it is a good solution.


A class can be created that inherites from Promise and adds an OnCancel to the executor parameter of the Promise constructor:

export class CancellablePromise<T> extends Promise<T> {
    private onCancel: () => void

    constructor(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void, onCancel: (cancelHandler: () => void) => void) => void) {
        let onCancel: () => void;
        super((rs, rj) => executor(rs, rj, (ch: () => void) => onCancel = ch));
        this.onCancel = onCancel;
    }

    public cancel() {
        if (this.onCancel) {
            this.onCancel();
        }
    }
}

Here is an example of usage:

public static search(query: string): CancellablePromise<Region[]> {
    return new CancellablePromise((resolve, reject, onCancel) => {
        const request = $.ajax({
            type: "GET",
            url: ROOT_URL + 'api/Regions',
            data: { q: query },
            success: (regions) => resolve(regions),
            error: (jqXHR) => reject(jqXHR.responseText),
        });
        onCancel(() => {
            if (request.state() == "pending") request.abort();
        });
    });
}

As you can see, this implementation can be constructed like any other promise. It can be used wherever a promise can and it includes a cancellation method.

Here is an example cancelling the promise:

// Cancel previous search (if any)
if (this.currentSearch) {
    this.currentSearch.cancel();
}

this.currentSearch = Regions.search(query);
this.currentSearch
    .then((regions) => {
        if (regions) {
            this.setState({ isSearching: false, regions: regions });
        } else {
            this.setState({ isSearching: false, regions: [] });
        }
    })
    .catch(() => this.setState({ isSearching: false, regions: [] }));

Promise Cancellation Is Dead — Long Live Promise Cancellation , A lot of people might think that means that promise cancellation… So what can you do? for the lack of CancelToken.source method to create a cancel token and it's paired cancel function. Typescript Generics Explained. Also, create a new folder named src inside the typescript folder. Simplify Async Callback Functions using Async/Await. Lets see how we can write a Promise and use it in async await. This method helps simplify the code inside functions like setTimeout. Create a new file inside src folder called index.ts.


I implemented something like this a few months ago. My solution does not exactly match your desired API but maybe it works for you or you can adapt it to your needs:

The Canceled error

First of all you should be aware that it is a bad idea to have a promise which is not resolved and not rejected at all. A third state (canceled) will break existing code and an application which is not changed to explicitly look for cancelation will hang forever. That's why I reject a promise with a special Canceled error when the promise is canceled. A Cancelation-aware reject handler can distinguish a failure from cancelation by looking at the error type (With instanceof). A cancelation-unaware reject handler will treat cancelation like an error.

Here is a simple implementation of such a custom error class:

class Canceled extends Error {
    constructor(reason: string = "") {
        super(reason);
        Object.setPrototypeOf(this, Canceled.prototype);
    }
}

The setPrototypeOf call is needed so instanceof can be used to detect an instance of this error type.

The Cancelable interface

Next you need an interface for the cancelable promise. Mine is pretty much like yours:

interface Cancelable<T> extends Promise<T> {
    cancel(reason?: string): Cancelable<T>;
}
The cancelable function

Creating an implementation class of the Cancelable interface is a bad idea. It is hard to inherit from the built-in Promise type. So I decided to use a standard Promise object and simply add the cancel method to the Promise instance instead of creating a new type. For this I use this function:

function cancelable<T>(promise: Promise<T>, onCancel?: (canceled: Canceled) => void): Cancelable<T> {
    let cancel: ((reason: string) => Cancelable<T>) | null = null;
    let cancelable: Cancelable<T>;
    cancelable = <Cancelable<T>>new Promise((resolve, reject) => {
        cancel = (reason: string = "") => {
            try {
                if (onCancel) {
                    onCancel(new Canceled(reason));
                }
            } catch (e) {
                reject(e);
            }
            return cancelable;
        };
        promise.then(resolve, reject);
    });
    if (cancel) {
        cancelable.cancel = cancel;
    }
    return cancelable;
}

So this function takes a normal Promise as first parameter and an onCancel callback as second parameter which is called when the cancel() method is called on the returned Cancelable.

Creating a cancelable promise

To actually create a cancelable promise you have to wrap a standard promise and your cancel handler with the cancelable function. Here is an example sleep function which is resolved after the given time (Using setTimeout) and which can be canceled (which clears the timeout with clearTimeout):

function sleep(ms: number): Cancelable<void> {
    let timer: any;
    return cancelable(new Promise(resolve => {
        timer = setTimeout(resolve, ms);
    }), canceled => {
        clearTimeout(timer);
        throw canceled;
    });
}

Notice that the onCancel callback receives a canceled argument which is an instance of the Canceled error type. The onCancel callback must throw this error when the promise was canceled successfully. If the promise can't be canceled for some reason then simply don't throw the error and the promise will continue its usual work.

How to cancel the promise

And finally here is an example how to cancel a cancelable promise and how to react on it:

const waiting = sleep(1000);
waiting.then(() => {
    console.log("Wait successful");
}, error => {
    if (error instanceof Canceled) {
        console.log("Wait canceled. Reason: " + error.message);
    } else {
        console.error("Wait failed:", error);
    }
});

waiting.cancel("Nah, stop waiting");

I hope this works for you or at least gives you some ideas how to make your own implementation.

How to Cancel Your Promise, We won't cover them here, except for the cancel method, which does cancel method on it (or monkey-patch instance of Promise, but it can create you that you are using all possible async approaches in a single codebase;� I have next code: export const getPath = async (from: CurrencyTicker, to: CurrencyTicker): Promise Tagged with typescript, javascript.


Solution from @kayahr really smart and elegant. But I would not use because it's quite rude to break thread inside promise's executer.

Who knows, maybe there are some IO operations. Reading/writing operations. And such cancelation might be dangerous. I mean file could be not closed as well or something like it.

In my work, I also was needed a "cancelable" promise. I did a little bit other solution.

The idea is, it isn't truly canceled, this is a way to have one more option of results. Actually promise gives us 2 + 1 ways:

  1. then: all done, all ok
  2. catch: ohh, error
  3. finally: do it, just do it

But if by some reason we would like to stop operation, we don't have such option.

With this solution, it's possible.

export type TResolver<T> = (value?: T) => void;
export type TRejector = (error: Error) => void;
export type TFinally = () => void;
export type TCanceler<T> = (reason?: T) => void;

export class CancelablePromise<T, C> {

    private _resolvers: Array<TResolver<T>> = [];
    private _rejectors: TRejector[] = [];
    private _cancelers: Array<TCanceler<C>> = [];
    private _finishes: TFinally[] = [];
    private _canceled: boolean = false;
    private _resolved: boolean = false;
    private _rejected: boolean = false;
    private _finished: boolean = false;

    constructor(
        executor: (resolve: TResolver<T>, reject: TRejector, cancel: TCanceler<C>, self: CancelablePromise<T, C>) => void,
    ) {
        const self = this;
        // Create and execute native promise
        new Promise<T>((resolve: TResolver<T>, reject: TRejector) => {
            executor(resolve, reject, this._doCancel.bind(this), self);
        }).then((value: T) => {
            this._doResolve(value);
        }).catch((error: Error) => {
            this._doReject(error);
        });
    }

    public then(callback: TResolver<T>): CancelablePromise<T, C> {
        this._resolvers.push(callback);
        return this;
    }

    public catch(callback: TRejector): CancelablePromise<T, C> {
        this._rejectors.push(callback);
        return this;
    }

    public finally(callback: TFinally): CancelablePromise<T, C> {
        this._finishes.push(callback);
        return this;
    }

    public cancel(callback: TCanceler<C>): CancelablePromise<T, C> {
        this._cancelers.push(callback);
        return this;
    }

    public break(reason: C): CancelablePromise<T, C> {
        this._doCancel(reason);
        return this;
    }

    private _doResolve(value: T) {
        if (this._canceled) {
            return;
        }
        this._resolved = true;
        this._resolvers.forEach((resolver: TResolver<T>) => {
            resolver(value);
        });
        this._doFinally();
    }

    private _doReject(error: Error) {
        if (this._canceled) {
            return;
        }
        this._rejected = true;
        this._rejectors.forEach((rejector: TRejector) => {
            rejector(error);
        });
        this._doFinally();
    }

    private _doFinally() {
        if (this._finished) {
            return;
        }
        this._finished = true;
        this._finishes.forEach((handler: TFinally) => {
            handler();
        });
    }

    private _doCancel(reason?: C) {
        if (this._resolved || this._rejected || this._canceled) {
            // Doesn't make sence to cancel, because it was resolved or rejected or canceled already
            return this;
        }
        this._canceled = true;
        this._cancelers.forEach((cancler: TCanceler<C>) => {
            cancler(reason);
        });
        this._doFinally();
    }

}

Let's see how it works.

const a: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel) => {
    // Do some long operation
    setTimeout(() => {
        resolve();
    }, 1000);
}).then(() => {
    console.log('resolved');
}).finally(() => {
    console.log('finally done');
});

Pretty much like normal promise. In output:

resolved
finally done

Let's try reject:

const b: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel) => {
    // Do some long operation
    setTimeout(() => {
        console.log('timer #1');
        resolve();
    }, 1000);
    setTimeout(() => {
        console.log('timer #2');
        reject(new Error('Because I can!'));
    }, 500);
}).then(() => {
    console.log('resolved');
}).catch((error: Error) => {
    console.log('error');
}).finally(() => {
    console.log('finally done');
});

Output:

timer #2
error
finally done
timer #1

Now we will cancel it

const c: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel) => {
    // Do some long operation
    setTimeout(() => {
        console.log('timer #1');
        resolve();
    }, 1000);
    setTimeout(() => {
        console.log('timer #2');
        reject(new Error('Because I can!'));
    }, 500);
    setTimeout(() => {
        console.log('timer #3');
        cancel();
    }, 250);
}).then(() => {
    console.log('resolved');
}).cancel(() => {
    console.log('canceled');
}).catch((error: Error) => {
    console.log('error');
});

Output

timer #3
timer #2
canceled
timer #1

Good we don't have resolve or reject fired, but (!) our executer still worked after promise was canceled. It's not good and here is a way how to fix it and cancel it in true way.

const d: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel, self) => {
    // Listen cancel inside executer to cancel all correctly
    self.cancel(() => {
        clearTimeout(t1);
        clearTimeout(t2);
    });
    // Do some long operation
    const t1: any = setTimeout(() => {
        console.log('timer #1');
        resolve();
    }, 1000);
    const t2: any = setTimeout(() => {
        console.log('timer #2');
        reject(new Error('Because I can!'));
    }, 500);
    setTimeout(() => {
        console.log('timer #3');
        cancel();
    }, 250);
}).then(() => {
    console.log('resolved');
}).cancel(() => {
    console.log('canceled');
}).catch((error: Error) => {
    console.log('error');
});

Output

timer #3
canceled

So, timers #1 and #2 was stopped - promise was canceled carefully.

Sure we can cancel it outside also.

const d: CancelablePromise<void, void> = new CancelablePromise<void, void>((resolve, reject, cancel, self) => {
    // Listen cancel inside executer to cancel all correctly
    self.cancel(() => {
        clearTimeout(t1);
        clearTimeout(t2);
    });
    // Do some long operation
    const t1: any = setTimeout(() => {
        console.log('timer #1');
        resolve();
    }, 1000);
    const t2: any = setTimeout(() => {
        console.log('timer #2');
        reject(new Error('Because I can!'));
    }, 500);
}).then(() => {
    console.log('resolved');
}).cancel(() => {
    console.log('canceled');
}).catch((error: Error) => {
    console.log('error');
});

setTimeout(() => {
    console.log('timer #3 (outside)');
    d.break();
}, 250);

Result will be same:

timer #3 (outside)
canceled

Hope it will be useful.

sindresorhus/p-cancelable: Create a promise that can be , PCancelable#cancel(reason?) Type: Function. Cancel the promise and optionally provide a reason. The cancellation is synchronous. Calling it after the promise� If we talk about Promise, so it works the same way we make promises to others. In a real-world scenario, when we make a promise to somebody that means the surety of doing something in the future. TypeScript promise holds the future value either it will return success or gets rejected.


How about this implementation?

declare class CancelablePromise<T> extends Promise<T> {
  declare readonly cancel: () => void
}

interface Promise<T> {
  asCancelable(): CancelablePromise<T>
}


Promise.prototype.asCancelable = function () {
  let cancel: (reason: {cancelled: boolean}) => void
  const wrappedPromise = new Promise((resolve, reject) => {
    cancel = reject
    Promise.resolve(this)
      .then(resolve)
      .catch(reject)
  }) as any
  wrappedPromise.cancel = () => {
    cancel({cancelled: true})
  }
  return wrappedPromise as CancelablePromise<unknown>
}

Usage:

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
const waiting = sleep(1000).asCancelable()
waiting.then(() => {
  console.info('this never get called')
})
waiting.cancel()

You can already cancel ES6 Promises � GitHub, Promise.pending = Promise.race.bind(Promise, []) let cancel new Possible implementations/API: function cancellablePromise(executor) { let cancel var res = new Promise(function(fulfill, reject) { let handler function Doesn't the current implementation create a new Promise every time you cancel Typescript syntax:. @dx_over_dt The exceptions are if a post is marked "Community Wiki" indicating it's intended to serve as a collaborative post (e.g. like Wikipedia), or if there are serious issues with the post such as rude/abusive language, dangerous/harmful content (e.g. suggestions or code that are liable to give you a virus or get you arrested, etc.), or personal info like health records, phone numbers


So I am learning Angular 2 with typescript. I am reaching a point to write a mocking service which (I believe) should return a Promise if the service get the Object Successfully and Return an Erro


In this guide, I’m going to walk through exactly how we can work with promises in typescript. However, before we can talk about this I want to give more of an explanation on what promises are. Thankfully because of the way promises were created and how it’s named It’s pretty easy and straightforward to compare it to a real-world scenario


The promise resolves with a response object when the remote server responds with headers, but before the full response is downloaded. To read the full response, we should call the method response.text(): it returns a promise that resolves when the full text is downloaded from the remote server, with that text as a result.