Fetch 的手動(dòng)終止

今天在寫一個(gè)前端頁面的時(shí)候發(fā)現(xiàn),我需要實(shí)現(xiàn)一個(gè)類似 Google 搜索的聯(lián)想功能時(shí),我需要手動(dòng)終止自己發(fā)出去的請(qǐng)求,比如:

類似輸入聯(lián)想

在這個(gè)時(shí)候,假如用戶一直輸入,如果不做處理那么就會(huì)導(dǎo)致 1s 內(nèi)很多的請(qǐng)求,這是不合理的。比如我輸入 前端的, 假如不做處理,瀏覽器就會(huì)發(fā)起三個(gè)請(qǐng)求,分別是針對(duì) 前端前端的 三個(gè)字的請(qǐng)求,并且每一個(gè)都拿到結(jié)果然后渲染到頁面中,但是其實(shí),我們只需要拿到針對(duì) 前端的 的查詢結(jié)果就行了,這個(gè)時(shí)候我認(rèn)為比較好的做法是 當(dāng)檢測到用戶輸入時(shí),取消針對(duì)上一個(gè)搜索字的請(qǐng)求,于是,我查閱了相關(guān)的資料,首先因?yàn)槲沂褂玫氖?Fetch ,所以在 MDN 上查到了相關(guān)的資料那就是今天的主角 AbortController ,先看官方的 Demo,

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

var downloadBtn = document.querySelector('.download');
var abortBtn = document.querySelector('.abort');

downloadBtn.addEventListener('click', fetchVideo);

abortBtn.addEventListener('click', function() {
  controller.abort();
  console.log('Download aborted');
});

function fetchVideo() {
  ...
  fetch(url, {signal}).then(function(response) {
    ...
  }).catch(function(e) {
    reports.textContent = 'Download error: ' + e.message;
  })
}

很明顯,一目了然,能夠很清晰的知道,采用這種方式可以手動(dòng)終止 Fetch,但是可惜的是前端的新技術(shù)的通病, 瀏覽器支持力度, 從 MDN 上可以看到這個(gè)技術(shù)尚處于正在開發(fā)階段。

AbortController 的瀏覽器兼容力度

幾乎都是比較新的瀏覽器才會(huì)支持。

是不是很可惜,如此好的技術(shù)卻只能看著。但是,我們不一定非要使用官方的規(guī)范,我們可以使用第三方的模塊,比如 Axios ,在這個(gè)項(xiàng)目的 github 主頁,能看到關(guān)于 取消的使用,有興趣可以自己去查看并且使用。

后面是我自己想著只使用 Fetch 但是又能手動(dòng)終止的實(shí)現(xiàn),因?yàn)槟母杏X使用 Fetch 很清爽 ??。

我的思路其實(shí)也借鑒了上面兩種方式的思想,簡單的說就是重新封裝異步的請(qǐng)求,然后再通過 setter 去讓異步請(qǐng)求主動(dòng)的 reject

Talk is cheap. Show me the code :

function wrap (func, ...args) {
    const cancel = {};
    return () => {
        const promiseHandle = new Promise((resolve, reject) => {
            Object.defineProperty(cancel, 'signal', {
                set () {
                    reject('Abort');
                }
            })

            func(...args).then(v => resolve(v)).catch(err => reject(err))
        });

        return Object.assign(promiseHandle, {
            cancel () {
                cancel.signal = true;
            }
        });
    }

}

寫得很簡陋,主要就是利用了 setter 來調(diào)用封裝的 Promise 主動(dòng)的執(zhí)行 setter 內(nèi)的 reject 函數(shù)。

完整示例:

function wrap (func, ...args) {
    const cancel = {};
    return () => {
        const promiseHandle = new Promise((resolve, reject) => {
            Object.defineProperty(cancel, 'signal', {
                set () {
                    reject('Abort');
                }
            })

            func(...args).then(v => resolve(v)).catch(err => reject(err))
        });

        return Object.assign(promiseHandle, {
            cancel () {
                cancel.signal = true;
            }
        });
    }

}

var func = wrap((v) => new Promise((res, rej) => {
    setTimeout(() => res(v), 5000);
}), 23333)

var a = func()

a.then(v => console.log(v)).catch(err => console.error(err))
a.cancel()

感覺使用 setter 這個(gè)有點(diǎn)不太優(yōu)雅,那么在 node 環(huán)境中,我們可以借助 events 這個(gè)內(nèi)置庫來實(shí)現(xiàn)同樣的效果,而且代碼看起來更優(yōu)雅

const EventEmitter = require('events');

function wrap (func, ...args) {
    const emitter = new EventEmitter();
    return () => {
        const promiseHandle = new Promise((resolve, reject) => {
            emitter.on('cancel', () => {
                reject(new Error('Reject'));
            })
            func(...args).then(v => resolve(v)).catch(err => reject(err))
        });

        return Object.assign(promiseHandle, {
            cancel () {
                emitter.emit('cancel');
            }
        });
    }

}

var func = wrap((v) => new Promise((res, rej) => {
    setTimeout(() => res(v), 5000);
}), 23333)

var a = func()

a.then(v => console.log(v)).catch(err => console.error(err))
a.cancel()

同理,還可以用 Rxjs 的觀察者模式來做,基本原理看到這里應(yīng)該很清晰了吧,就是封裝成一個(gè) Promise 然后再定義一個(gè)函數(shù),可以觸發(fā) reject , 最后再暴露出一個(gè)函數(shù)用于觸發(fā) Promise 里面的函數(shù)。說得有點(diǎn)繞,其實(shí)原理很簡單。

假如有錯(cuò)誤之處,請(qǐng)指正,謝謝??!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,547評(píng)論 8 265
  • 本文詳細(xì)介紹了 XMLHttpRequest 相關(guān)知識(shí),涉及內(nèi)容: AJAX、XMLHTTP、XMLHttpReq...
    semlinker閱讀 13,720評(píng)論 2 18
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,868評(píng)論 18 139
  • 【轉(zhuǎn)載】CSDN - 張林blog http://blog.csdn.net/XIAOZHUXMEN/articl...
    竿牘閱讀 3,507評(píng)論 1 14
  • 天色陰沉,仿佛一條浸了水的毛巾,蓄滿了力量,上帝卻沒有用手?jǐn)Q一下,懸在頭頂,充滿著一種緊迫感。 緊迫感,我想這是對(duì)...
    一葉孤生閱讀 181評(píng)論 0 1