DEV Community

Discussion on: How to cancel a promise in javascript ?

Collapse
digitalbrainjs profile image
Dmitriy Mozgovoy • Edited on

Using 'c-promise2' package we can do the following trick:
(Live Demo - jsfiddle.net/DigitalBrain/c6njyrt9... )

import CPromise from "c-promise2";

function fetchWithTimeout(url, {timeout, ...fetchOptions}= {}) {
   return new CPromise((resolve, reject, {signal}) => {
      fetch(url, {...fetchOptions, signal}).then(resolve, reject)
   }, timeout)
}

const promise= fetchWithTimeout('http://localhost/', {timeout: 5000})
      .then(response => response.json())
      .then(data => console.log(`Done: `, data), err => console.log(`Error: `, err))

setTimeout(()=> promise.cancel(), 1000);  // cancel the pending chain when you need
// yes, the related network request will be canceled too

// CPromise.race([
//    fetchWithTimeout(url1), fetchWithTimeout(url2), fetchWithTimeout(url3)
// ]).then(console.log); 
// The other two slower network requests will be aborted as well
Enter fullscreen mode Exit fullscreen mode

Simple React Demo of using AbortController signal - codesandbox.io/s/react-fetch-class...

export class AsyncComponent extends React.Component {
    state = {};

    async componentDidMount() {
        console.log("mounted");
        this.controller = new CPromise.AbortController();
        try {
            await this.myAsyncTask1();
            const result= await this.myAsyncTask2();
            this.setState({ text: result });
        } catch (err) {
            if (CPromise.isCanceledError(err)) {
                console.log("tasks terminated");
                return;
            }
            throw err;
        }
    }

    myAsyncTask1() {
        return CPromise.from(function* () {
            yield ...
            yield ...
        }).listen(this.controller.signal);
    }

    myAsyncTask2() {
        return new CPromise((resolve, reject, { onCancel }) => {
            //code
        }).listen(this.controller.signal);
    }

    componentWillUnmount() {
        console.log("unmounted");
        this.controller.abort(); // kill all tasks
    }
}
Enter fullscreen mode Exit fullscreen mode

Simple React Demo using hooks - codesandbox.io/s/react-cpromise-fe...

import React, { useEffect, useState } from "react";
import CPromise from "c-promise2";
import cpFetch from "cp-fetch";

function MyComponent(props) {
    const [text, setText] = useState("fetching...");

    useEffect(() => {
        console.log("mount");
        const promise = cpFetch(props.url)
            .then((response) => response.json())
            .then((value) => CPromise.delay(1000, value)) //one more dummy task
            .then((json) => setText(`Success: ${JSON.stringify(json)}`))
            .canceled() // catch CanceledError
            .catch((err) => {
                setText(`Failed: ${err}`);
            });

        return () => {
            console.log("unmount");
            promise.cancel();
        };
    }, [props.url]);

    return <p>{text}</p>;
}
Enter fullscreen mode Exit fullscreen mode