搜尋
首頁web前端js教程與承諾並行預加載圖像

Preloading Images in Parallel with Promises

核心要點

  • 使用Promise異步加載圖片,允許同時加載不同圖片集合,並在集合加載完成後執行代碼。這通過減少整體加載時間來顯著提高網站性能。
  • 此技術涉及為所有圖片“組”(集合)創建一個共享預加載器,該預加載器將要加載的圖片排隊。然後,預加載器並行(而非順序)開始加載圖片,避免必須等待一個組完成才能開始下一個組。
  • 每個圖片URL都替換為一個Promise,該Promise在瀏覽器加載圖片後解析。然後,可以使用Promise.all()方法為每個組創建一個Promise,該Promise在數組中的所有Promise都解析後解析。
  • 通過使用延遲Promise而不是回調來告訴預加載器在組加載完成後該做什麼,可以進一步改進該技術。這允許稍後控制Promise的解析。

本文探討一個具體問題:如何並行預加載大量圖片。 我最近遇到了這個問題,發現它比最初預期的更具挑戰性,也從中學習了很多。首先,讓我簡要描述一下場景。假設頁面上有幾個“組”。廣義上說,一個組就是一個圖片集合。我們希望預加載每個組的圖片,並能夠知道何時完成某個組的圖片加載。此時,我們可以自由運行任何我們想要的代碼,例如向組添加一個類、運行圖像序列、記錄某些內容等等。起初,這聽起來很簡單,甚至非常簡單。但是,你可能和我一樣忽略了一個細節:我們希望所有組並行加載,而不是順序加載。換句話說,我們不希望先加載組1的所有圖片,然後加載組2的所有圖片,再加載組3的所有圖片,依此類推。事實上,這不是理想的,因為最終會有一些組需要等待前面的組完成。因此,在一個場景中,如果第一個組有幾十張圖片,而第二個組只有一兩張圖片,我們就必須等待第一個組完全加載才能準備第二個組。這不好。我們肯定可以做得更好!所以我們的想法是並行加載所有組,這樣當一個組完全加載時,我們不必等待其他組。為此,大致思路是加載所有組的第一張圖片,然後加載所有組的第二張圖片,依此類推,直到所有圖片都已預加載。好了,讓我們從創建一些標記開始,這樣我們就能就正在發生的事情達成一致。

順便說一句,在本文中,我假設您熟悉Promise的概念。如果不是這樣,我建議您閱讀這篇文章。

標記

從標記的角度來看,一個組只不過是一個元素(例如div),帶有deck類以便我們可以定位它,以及一個包含圖片URL數組(作為JSON)的data-images屬性。

<div class="deck" data-images='["...", "...", "..."]'>...</div>
<div class="deck" data-images='["...", "..."]'>...</div>
<div class="deck" data-images='["...", "...", "...", "..."]'>...</div>

準備工作

在JavaScript方面,這——不出所料——有點複雜。我們將構建兩樣不同的東西:一個組(請將此放在非常大的引號之間,不要對術語吹毛求疵)和一個預加載器工具。因為預加載器必須知道所有組的所有圖片才能以特定的順序加載它們,所以它需要在所有組之間共享。一個組不能有它自己的預加載器,否則我們會遇到最初的問題:代碼是順序執行的,這不是我們想要的。所以我們需要一個傳遞給每個組的預加載器。後者將它的圖片添加到預加載器的隊列中,一旦所有組都將它們的項目添加到隊列中,預加載器就可以開始預加載。執行代碼片段如下:

// 实例化一个预加载器
var ip = new ImagePreloader();
// 从DOM获取所有组
var decks = document.querySelectorAll('.deck');

// 遍历它们并为每个组实例化一个新的组,将预加载器传递给每个组,以便组可以将它的图片添加到队列中
Array.prototype.slice.call(decks).forEach(function (deck) {
  new Deck(deck, ip);
});

// 一旦所有组都将它们的项目添加到队列中,就预加载所有内容
ip.preload();

我希望到目前為止,這是有意義的!

構建組

根據您想對組做什麼,這個“類”可能相當長。對於我們的場景,我們唯一要做的事情是在其圖片加載完成後向節點添加一個loaded類。 Deck函數沒有太多工作要做:1. 加載數據(從data-images屬性);2. 將數據添加到預加載器隊列的末尾;3. 告訴預加載器在數據預加載完成後該做什麼。

var Deck = function (node, preloader) {
  // 我们从`data-images`属性获取并解析数据
  var data = JSON.parse(node.getAttribute('data-images'));

  // 我们调用预加载器的`queue`方法,将数据和回调函数传递给它
  preloader.queue(data, function () {
    node.classList.add('loaded');
  });
};

到目前為止,進展順利,不是嗎?唯一剩下的就是預加載器,儘管它也是本文中最複雜的代碼部分。

構建預加載器

我們已經知道我們的預加載器需要一個queue方法來將圖片集合添加到隊列中,以及一個preload方法來啟動預加載。它還需要一個輔助函數來預加載圖片,稱為preloadImage。讓我們從這裡開始:

var ImagePreloader = function () { ... };
ImagePreloader.prototype.queue = function () { ... }
ImagePreloader.prototype.preloadImage = function () { ... }
ImagePreloader.prototype.preload = function () { ... }

預加載器需要一個內部queue屬性來保存它必須預加載的組,以及它們各自的回調。

var ImagePreloader = function () {
  this.items = [];
}

items是一個對像數組,其中每個對像有兩個鍵:- collection包含要預加載的圖片URL數組;- callback包含在組完全加載後要執行的函數。

知道了這一點,我們可以編寫queue方法。

<div class="deck" data-images='["...", "...", "..."]'>...</div>
<div class="deck" data-images='["...", "..."]'>...</div>
<div class="deck" data-images='["...", "...", "...", "..."]'>...</div>

好了。此時,每個組都可以將它的圖片添加到隊列中。我們現在必須構建preload方法,它將負責實際預加載圖片。但在跳轉到代碼之前,讓我們退一步來理解我們需要做什麼。我們的想法不是一個接一個地預加載每個組的所有圖片。我們的想法是預加載每個組的第一張圖片,然後是第二張,然後是第三張,依此類推。 預加載一張圖片意味著使用JavaScript(使用new Image())創建一個新的圖片,並為其應用一個src。這將提示瀏覽器異步加載源。由於這個異步過程,我們需要註冊一個Promise,該Promise在瀏覽器下載資源後解析。基本上,我們將用一個Promise替換我們數組中的每個圖片URL,該Promise在瀏覽器加載給定圖片後解析。此時,我們將能夠使用Promise.all(..) 來獲得一個最終的Promise,該Promise在數組中的所有Promise都解析後解析。對於每個組都是如此。讓我們從preloadImage方法開始:

// 实例化一个预加载器
var ip = new ImagePreloader();
// 从DOM获取所有组
var decks = document.querySelectorAll('.deck');

// 遍历它们并为每个组实例化一个新的组,将预加载器传递给每个组,以便组可以将它的图片添加到队列中
Array.prototype.slice.call(decks).forEach(function (deck) {
  new Deck(deck, ip);
});

// 一旦所有组都将它们的项目添加到队列中,就预加载所有内容
ip.preload();

現在是preload方法。它做兩件事(因此可能可以拆分成兩個不同的函數,但這不在本文的範圍內):1. 它以特定的順序(每個組的第一張圖片,然後是第二張,然後是第三張……)將所有圖片URL替換為Promise;2. 對於每個組,它註冊一個Promise,當組中的所有Promise都解析後(!)調用組的回調。

var Deck = function (node, preloader) {
  // 我们从`data-images`属性获取并解析数据
  var data = JSON.parse(node.getAttribute('data-images'));

  // 我们调用预加载器的`queue`方法,将数据和回调函数传递给它
  preloader.queue(data, function () {
    node.classList.add('loaded');
  });
};

就是這樣!畢竟沒有那麼複雜,你同意嗎?

進一步推進

代碼運行良好,儘管使用回調來告訴預加載器在組加載完成後該做什麼並不是很優雅。您可能希望使用Promise而不是回調,尤其是在我們一直使用Promise的情況下!我不確定如何解決這個問題,所以我不得不承認我請我的朋友Valérian Galliat幫我解決這個問題。我們在這裡使用的是延遲Promise。延遲Promise不是原生Promise API的一部分,因此我們需要為其添加polyfill;謝天謝地,這只需要幾行代碼。基本上,延遲Promise是一個稍後可以解析的Promise。將其應用於我們的代碼,只會改變很少的東西。首先是.queue(..)方法:

var ImagePreloader = function () { ... };
ImagePreloader.prototype.queue = function () { ... }
ImagePreloader.prototype.preloadImage = function () { ... }
ImagePreloader.prototype.preload = function () { ... }

.preload(..)方法中的解析:

var ImagePreloader = function () {
  this.items = [];
}

當然,最後是我們添加數據到隊列的方式!

// 如果没有指定回调,则为空函数
function noop() {}

ImagePreloader.prototype.queue = function (array, callback) {
  this.items.push({
    collection: array,
    // 如果没有回调,我们推送一个no-op(空)函数
    callback: callback || noop
  });
};

我們完成了!如果您想查看代碼的實際運行情況,請查看下面的演示:(此處應插入CodePen演示鏈接,因為我無法直接嵌入CodePen)

結論

好了,朋友們。大約70行JavaScript代碼,我們就成功地異步並行加載了不同集合中的圖片,並在集合加載完成後執行了一些代碼。從這裡開始,我們可以做很多事情。在我的例子中,重點是在點擊按鈕時將這些圖片作為快速循環序列(gif樣式)運行。因此,我在加載期間禁用了按鈕,並在組完成所有圖片的預加載後重新啟用它。由於瀏覽器已經緩存了所有圖片,因此第一個循環運行非常流暢。我希望你喜歡它!您可以在GitHub上查看代碼,也可以直接在CodePen上使用它。 (此處應插入GitHub鏈接和CodePen鏈接)

(此處應添加FAQ部分,與輸入文本中的FAQ部分內容一致,但語言表達上進行了一些調整和潤色。)

以上是與承諾並行預加載圖像的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript數據類型:瀏覽器和nodejs之間是否有區別?JavaScript數據類型:瀏覽器和nodejs之間是否有區別?May 14, 2025 am 12:15 AM

JavaScript核心數據類型在瀏覽器和Node.js中一致,但處理方式和額外類型有所不同。 1)全局對像在瀏覽器中為window,在Node.js中為global。 2)Node.js獨有Buffer對象,用於處理二進制數據。 3)性能和時間處理在兩者間也有差異,需根據環境調整代碼。

JavaScript評論:使用//和 / * * / * / * /JavaScript評論:使用//和 / * * / * / * /May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript:開發人員的比較分析Python vs. JavaScript:開發人員的比較分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要區別在於類型系統和應用場景。 1.Python使用動態類型,適合科學計算和數據分析。 2.JavaScript採用弱類型,廣泛用於前端和全棧開發。兩者在異步編程和性能優化上各有優勢,選擇時應根據項目需求決定。

Python vs. JavaScript:選擇合適的工具Python vs. JavaScript:選擇合適的工具May 08, 2025 am 12:10 AM

選擇Python還是JavaScript取決於項目類型:1)數據科學和自動化任務選擇Python;2)前端和全棧開發選擇JavaScript。 Python因其在數據處理和自動化方面的強大庫而備受青睞,而JavaScript則因其在網頁交互和全棧開發中的優勢而不可或缺。

Python和JavaScript:了解每個的優勢Python和JavaScript:了解每個的優勢May 06, 2025 am 12:15 AM

Python和JavaScript各有優勢,選擇取決於項目需求和個人偏好。 1.Python易學,語法簡潔,適用於數據科學和後端開發,但執行速度較慢。 2.JavaScript在前端開發中無處不在,異步編程能力強,Node.js使其適用於全棧開發,但語法可能複雜且易出錯。

JavaScript的核心:它是在C還是C上構建的?JavaScript的核心:它是在C還是C上構建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; sanInterpretedlanguagethatrunsonenginesoftenwritteninc.1)JavascriptwasdesignedAsignedAsalightWeight,drackendedlanguageforwebbrowsers.2)Enginesevolvedfromsimpleterterpretpretpretpretpreterterpretpretpretpretpretpretpretpretpretcompilerers,典型地,替代品。

JavaScript應用程序:從前端到後端JavaScript應用程序:從前端到後端May 04, 2025 am 12:12 AM

JavaScript可用於前端和後端開發。前端通過DOM操作增強用戶體驗,後端通過Node.js處理服務器任務。 1.前端示例:改變網頁文本內容。 2.後端示例:創建Node.js服務器。

Python vs. JavaScript:您應該學到哪種語言?Python vs. JavaScript:您應該學到哪種語言?May 03, 2025 am 12:10 AM

選擇Python還是JavaScript應基於職業發展、學習曲線和生態系統:1)職業發展:Python適合數據科學和後端開發,JavaScript適合前端和全棧開發。 2)學習曲線:Python語法簡潔,適合初學者;JavaScript語法靈活。 3)生態系統:Python有豐富的科學計算庫,JavaScript有強大的前端框架。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),