使用abortController終止fetch和promise


使用 abortController 終止 fetch 和 promise

在使用 fetch 和 promise 的時候,中途終止它們是一個很常見的需求,我們來看一看怎么實現。通過本文您可以學到:

  1. 怎么在外面終止 xhr 請求
  2. abortController 是什么
  3. 怎么使用 abortController 終止 fetch 請求
  4. 怎么使用 abortController 終止 promise 請求

怎么在外部終止 xhr 請求

我們先來回顧一下怎么在外部終止 xhr 請求:

let xhr = new XMLHttpRequest();
xhr.onload = (res) => console.log(res);
xhr.onerror = (err) => console.log(err);
xhr.onabort = () => console.log('aborted!');
xhr.open('get', 'https://slowmo.glitch.me/5000');
xhr.send();

setTimeout(() => {
    xhr.abort();
}, 200);

AbortController

參考資料:abortController接口

AbortController 接口表示一個控制器對象,允許你根據需要中止一個或多個 Web 請求。目前這個接口的兼容性是除了IE已經能夠兼容所有主流瀏覽器了。示例如下:

const controller = new AbortController();
const signal = controller.signal;

signal.addEventListener('abort', () => {
    console.log('aborted!');
});

controller.abort();

中止 fetch 請求

fetch 方法的第二個參數可以接收 signal 參數,當被中止時,會 reject 一個名字為 AbortError 的 error 並被 catch 捕捉到,示例如下:

const controller = new AbortController();
const signal = controller.signal;

fetch('https://slowmo.glitch.me/5000', { signal })
    .then(res => res.json())
    .then(res => console.log(res))
    .catch((err) => {
        if (err.name === 'AbortError') {
            console.log('aborted');
        } else {
            console.log('error');
        }
    });

setTimeout(() => {
    controller.abort();
}, 200);

中止 promise

其實不需要 AbortController也可以實現手動中止 promise,還是用那套劫持的方法。示例如下:

class MyPromise {
    constructor(executor) {
        let abort = null;
        let p = new Promise((resovle, reject)=>{
            executor(resovle, reject);
            abort = err => reject(err);
        })

        p.abort = abort;
        return p;
    }
}

// test
let test = new MyPromise((resolve) => {
    setTimeout(() => resolve(1), 200);
});

// 這里不能直接把 then 和 catch 加到上面的末尾去
test.then(res => console.log(res))
.catch(err => console.log(err));

test.abort('aborted!');

上面有一些不完美,就是在初始化 test 的時候,后面不能帶上 then 或 catch 方法,因為這些方法返回的是一個 Promise 實例而不是 MyPromise 實例。

使用 AbortController 可以解決這個問題,示例如下:

class MyPromise {
    constructor(executor, { signal }) {
        return new Promise((resolve, reject)=>{
            executor(resolve, reject);

            if (signal) {
                signal.addEventListener('abort', () => {
                    reject('aborted!');
                });
            }
        })
    }
}

// test
const controller = new AbortController();
const signal = controller.signal;

let test = new MyPromise((resolve) => {
    setTimeout(() => resolve(1), 200);
}, { signal })
.then(res => console.log(res))
.catch(err => console.log(err));

controller.abort('aborted!');

上面兩種方案看起來很美好,但是其實並不完美,因為 then 和 catch 方法返回的是一個 Promise 實例而不是 MyPromise 實例,所以目前這個 abort 方法不能中斷 then 或 catch 里面的內容,如果要修正的話,需要再繼續改寫 then 和 catch 方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM