欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

如何實(shí)現(xiàn) xhr 和 fetch 的加載進(jìn)度條功能?

freeflydom
2023年11月27日 11:45 本文熱度 704

想要在 xhr 和 fetch 中獲得數(shù)據(jù)加載的比例,從而實(shí)現(xiàn)一個(gè)“真”進(jìn)度條,你有什么實(shí)現(xiàn)思路嗎?

我是渡一前端子辰老師,相信認(rèn)真閱讀完這篇文章后,這將不再是一個(gè)問(wèn)題!

思考

首先,我們知道數(shù)據(jù)加載的比例常用在進(jìn)度條的效果上。

這就意味著我們需要監(jiān)聽(tīng)從響應(yīng)開(kāi)始到響應(yīng)完成,這個(gè)過(guò)程中任意一個(gè)時(shí)間點(diǎn)上目前加載數(shù)據(jù)的多少,以及總量的多少。

因?yàn)橹灰懒四壳暗牧恳约翱偭浚覀兙湍軌虻玫饺我鈺r(shí)間點(diǎn)的加載進(jìn)度。

得到進(jìn)度之后剩下的就是渲染界面了,這部分就比較簡(jiǎn)單了。

那么關(guān)鍵點(diǎn)就在于封裝 Ajax 請(qǐng)求,我們?nèi)绾畏謩e在 xhr 與 fetch 中得到目前量與總量?會(huì)遇到什么問(wèn)題呢?我們先從 xhr 開(kāi)始。

xhr 中的進(jìn)度

我們先看一個(gè)最常見(jiàn)的 xhr 的封裝。

export function request(options = {}) {

  const { url, method = "GET", data = null } = options;

  return new Promise((resolve) => {

    const xhr = new XMLHttpRequest();

    xhr.addEventListener("readystatechange", () => {

      if (xhr.readyState === xhr.DONE) {

        resolve(xhr.responseText);

      }

    });

    xhr.open(method, url);

    xhr.send(data);

  });

}

這樣的封裝我們無(wú)法知曉目前服務(wù)器傳輸了多少數(shù)據(jù),所有我們來(lái)改造一下。

export function request(options = {}) {

  // 首先我們?cè)谂渲美锛尤胍粋€(gè) onProgress

  // 這個(gè) onProgress 要傳遞一個(gè)函數(shù)

  // 沒(méi)每當(dāng)服務(wù)器完成了一小段數(shù)據(jù)的加載之后,我們就會(huì)調(diào)用這個(gè)函數(shù)

  // 并且返回目前的加載量以及總量

  const { url, method = "GET", onProgress, data = null } = options;

  return new Promise((resolve) => {

    const xhr = new XMLHttpRequest();

    xhr.addEventListener("readystatechange", () => {

      if (xhr.readyState === xhr.DONE) {

        resolve(xhr.responseText);

      }

    });

    // xhr 給我們提供了一個(gè) progress 事件,這里的 progress 事件只監(jiān)聽(tīng)響應(yīng)。

    // 每當(dāng)服務(wù)器傳輸完一小段數(shù)據(jù)之后就會(huì)觸發(fā) progress 事件

    xhr.addEventListener("progress", (e) => {

      // 在事件 e 里包含了總量與加載量,我們打印到控制臺(tái)

      // e.loaded 當(dāng)前加載量

      // e.total 總量

      console.log(e.loaded, e.total);

    });

    xhr.open(method, url);

    xhr.send(data);

  });

}

可以看到,每一次加載完一小段,都會(huì)輸出加載量和總值,那么知道了這兩個(gè)數(shù)據(jù)之后,計(jì)算百分比就很簡(jiǎn)單了。

我們只需要將數(shù)據(jù)返回給 onProgress 在界面實(shí)現(xiàn)效果就好了。

export function request(options = {}) {

  const { url, method = "GET", onProgress, data = null } = options;

  return new Promise((resolve) => {

    const xhr = new XMLHttpRequest();

    xhr.addEventListener("readystatechange", () => {

      if (xhr.readyState === xhr.DONE) {

        resolve(xhr.responseText);

      }

    });

    xhr.addEventListener("progress", (e) => {

      // 調(diào)用 onProgress 并將數(shù)據(jù)傳遞給它

      onProgress &&

        onProgress({

          loaded: e.loaded,

          total: e.total,

        });

    });

    xhr.open(method, url);

    xhr.send(data);

  });

}

于是我們就得到了這樣一個(gè)效果,接下來(lái)我們看看 fetch 中如何實(shí)現(xiàn)。

fetch 中的進(jìn)度

我們?cè)賮?lái)看一個(gè)非常簡(jiǎn)單的 fetch 封裝。

export function request(options = {}) {

  const { url, method = "GET", data = null } = options;

  return new Promise(async (resolve) => {

    const resp = await fetch(url, {

      method,

      body: data,

    });

    const body = await resp.text();

    resolve(body);

  });

}

因?yàn)?fetch 返回的是一個(gè) Promise,它沒(méi)有提供任何事件,所以我們獲取到加載量是很困難的,而 Promise 最終只有兩種狀態(tài),要么成功,要么失敗。

我們無(wú)法知道從開(kāi)始到成功或從開(kāi)始到失敗中間發(fā)生了什么事情。

但是我們知道服務(wù)器端的響應(yīng)頭里有一個(gè) Content-Length 字段,這個(gè)字段向我們預(yù)告了給我們的數(shù)據(jù)一共有多少個(gè)字節(jié)。

所以說(shuō)總得數(shù)據(jù)量我們是知道的。

關(guān)鍵的是當(dāng)前的加載量我們不知道,那么我們就必須改造一下這個(gè) fetch 的封裝。

在改造之前先給同學(xué)說(shuō)一下流的概念,假設(shè)可讀流是一桶水,讀取流就是反復(fù)一杯一杯的從桶里盛出水,可讀流被讀取完就是桶里的水被盛完了。

好了,我們來(lái)改造一下 fetch。

export function request(options = {}) {

  const { url, method = "GET", data = null } = options;

  return new Promise(async (resolve) => {

    const resp = await fetch(url, {

      method,

      body: data,

    });

    // 因?yàn)槲覀儾恢?Promise 中間發(fā)生了什么,所以就不能使用這樣的方便時(shí)解析響應(yīng)體了

    // const body = await resp.text();

    // 如果說(shuō)你熟悉 fetch Api 應(yīng)該知道,

    // resp 對(duì)象里有個(gè)屬性叫 body 它代表的就是響應(yīng)體

    // resp.body 的類(lèi)型是一個(gè) ReadableStream<Uint8Array> 也就是可讀流

    // 那既然是一個(gè)可讀流,我們就通過(guò) getReader() 讀取一下,拿到流的讀取器

    const reader = resp.body.getReader();

    // 我們使用循環(huán)來(lái)讀取流的數(shù)據(jù)

    while (1) {

      // 讀取流是需要時(shí)間的,所以我們等待一下

      // 返回值是一個(gè)對(duì)象,我們結(jié)構(gòu)出來(lái)得到兩個(gè)值

      // value 是當(dāng)前流的數(shù)據(jù),done 是流數(shù)據(jù)我們是否讀取完畢

      const { value, done } = await reader.read();

      // 如果說(shuō)取完了就不再循環(huán)了

      if (done) {

        break;

      }

      // 我們打印一下流的數(shù)據(jù)

      console.log("value >>> ", value);

    }

    // 暫時(shí)禁用,不讓 Promise 完成

    // resolve(body);

  });

}

可以看到流數(shù)據(jù)在不停的被打印,每打印一次就像是可讀流里盛出的一杯水,每一杯水的量是不同的,它會(huì)根據(jù)你的網(wǎng)絡(luò)傳輸情況和你系統(tǒng)處理速度有關(guān)系,所以我們只要得到這個(gè)每一次讀取的量相加在一起,就得到了當(dāng)前讀取的量。

我們來(lái)繼續(xù)寫(xiě)一下。

export function request(options = {}) {

  // 在配置里加入一個(gè) onProgress

  const { url, method = "GET", onProgress, data = null } = options;

  return new Promise(async (resolve) => {

    const resp = await fetch(url, {

      method,

      body: data,

    });

    // 通過(guò) content-length 得到總量

    const total = +resp.headers.get("content-length");

    const reader = resp.body.getReader();

    // 聲明一個(gè)變量用來(lái)儲(chǔ)存讀取的量

    let loaded = 0;

    // promise 最后的完成需要把所有的數(shù)據(jù)拼接起來(lái)返回

    // 所以定一個(gè)變量用來(lái)儲(chǔ)存數(shù)據(jù)拼接的值

    let body = "";

    // 這個(gè)數(shù)據(jù)可能是二進(jìn)制,那就要使用 arrayBuffer

    // 也可能是文本,就要使用文本解碼器

    // 比如說(shuō)我們這里是文本,我們先定一個(gè)解碼器

    const decoder = new TextDecoder();

    while (1) {

      const { value, done } = await reader.read();

      if (done) {

        break;

      }

      // 每一次讀取都累加起來(lái)

      loaded += value.length;

      // 每一次讀取都對(duì)數(shù)據(jù)解碼并拼接起來(lái)

      body += decoder.decode(value);

      // 當(dāng)然在每一次讀取的時(shí)候都要像 xhr 一樣,把總量和讀取量返回

      onProgress &&

        onProgress({

          loaded,

          total,

        });

    }

    // Promise 完成并返回?cái)?shù)據(jù)

    resolve(body);

  });

}

代碼搞定了我們看一下結(jié)果。

擴(kuò)展

下載的進(jìn)度我們都實(shí)現(xiàn)了,那么你有沒(méi)有思考過(guò),上傳怎么辦?按照邏輯來(lái)說(shuō)下載和上傳應(yīng)該是一樣的,就是反著來(lái)的而已。

我們先來(lái)說(shuō) xhr,xhr 中就比較簡(jiǎn)單。

// xhr 中給我們提供了一個(gè)事件叫 upload

// upload 里有一個(gè)事件叫 progress, upload 里的 progress 事件只監(jiān)聽(tīng)請(qǐng)求。

// 它的事件 e 里仍然提供了

// e.loaded 和 e.total

// 所以 xhr 中實(shí)現(xiàn)上傳就比較簡(jiǎn)單

xhr.upload.addEventListener("progress", (e) => {});

我們?cè)趤?lái)說(shuō)一下 fetch,遺憾的是 fetch 中實(shí)現(xiàn)不了請(qǐng)求進(jìn)度。

有的同學(xué)會(huì)說(shuō),響應(yīng)是一個(gè) response 對(duì)象,它里邊有 body 可以拿到讀取器,可以一部分一部分的讀,那么請(qǐng)求不就是一個(gè) request 對(duì)象嗎?它里邊不也有 body 嗎?不也可以一部分一部分讀嗎?

這是不行的,子辰盡量給同學(xué)解釋一下,聽(tīng)不懂也沒(méi)關(guān)系。

我們知道,無(wú)論是請(qǐng)求或者響應(yīng),它的 body 屬性的類(lèi)型都是一個(gè)叫做 ReadableStream 的可讀流。

這種可讀流都有一個(gè)特點(diǎn),就是在同一時(shí)間只能被一個(gè)人讀取,那么你想想,請(qǐng)求里的流是不是被瀏覽器讀取了?瀏覽器把這個(gè)流讀出來(lái),然后發(fā)送到了服務(wù)器,所以說(shuō)我們就讀不了了,就是這個(gè)問(wèn)題。

而且瀏覽器在讀的過(guò)程中又不告訴我們它讀了多少,但是目前 W3C 正在討論一種方案,這種方案是附帶在 ServiceWorker 里邊的,它里邊有一套 API 叫做,BackgroundFetchManager目前這套 API 里可以實(shí)現(xiàn)請(qǐng)求進(jìn)度的監(jiān)聽(tīng),但是這套 API 還在試驗(yàn)中,不能用于生產(chǎn)環(huán)境。


作者:子辰Web草廬
鏈接:https://juejin.cn/post/7253969759191023675
來(lái)源:稀土掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。



該文章在 2023/11/27 11:45:02 編輯過(guò)
關(guān)鍵字查詢(xún)
相關(guān)文章
正在查詢(xún)...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專(zhuān)業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車(chē)隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類(lèi)企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷(xiāo)售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶(hù)的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved