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

與承諾並行預加載圖像

Feb 19, 2025 am 11:01 AM

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

熱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

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

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

前端熱敏紙小票打印遇到亂碼問題怎麼辦? 前端熱敏紙小票打印遇到亂碼問題怎麼辦? Apr 04, 2025 pm 02:42 PM

前端熱敏紙小票打印的常見問題與解決方案在前端開發中,小票打印是一個常見的需求。然而,很多開發者在實...

神秘的JavaScript:它的作用以及為什麼重要 神秘的JavaScript:它的作用以及為什麼重要 Apr 09, 2025 am 12:07 AM

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

誰得到更多的Python或JavaScript? 誰得到更多的Python或JavaScript? Apr 04, 2025 am 12:09 AM

Python和JavaScript開發者的薪資沒有絕對的高低,具體取決於技能和行業需求。 1.Python在數據科學和機器學習領域可能薪資更高。 2.JavaScript在前端和全棧開發中需求大,薪資也可觀。 3.影響因素包括經驗、地理位置、公司規模和特定技能。

如何實現視差滾動和元素動畫效果,像資生堂官網那樣?
或者:
怎樣才能像資生堂官網一樣,實現頁面滾動伴隨的動畫效果? 如何實現視差滾動和元素動畫效果,像資生堂官網那樣? 或者: 怎樣才能像資生堂官網一樣,實現頁面滾動伴隨的動畫效果? Apr 04, 2025 pm 05:36 PM

實現視差滾動和元素動畫效果的探討本文將探討如何實現類似資生堂官網(https://www.shiseido.co.jp/sb/wonderland/)中�...

JavaScript難以學習嗎? JavaScript難以學習嗎? Apr 03, 2025 am 12:20 AM

學習JavaScript不難,但有挑戰。 1)理解基礎概念如變量、數據類型、函數等。 2)掌握異步編程,通過事件循環實現。 3)使用DOM操作和Promise處理異步請求。 4)避免常見錯誤,使用調試技巧。 5)優化性能,遵循最佳實踐。

JavaScript的演變:當前的趨勢和未來前景 JavaScript的演變:當前的趨勢和未來前景 Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

如何使用JavaScript將具有相同ID的數組元素合併到一個對像中? 如何使用JavaScript將具有相同ID的數組元素合併到一個對像中? Apr 04, 2025 pm 05:09 PM

如何在JavaScript中將具有相同ID的數組元素合併到一個對像中?在處理數據時,我們常常會遇到需要將具有相同ID�...

前端開發中如何實現類似 VSCode 的面板拖拽調整功能? 前端開發中如何實現類似 VSCode 的面板拖拽調整功能? Apr 04, 2025 pm 02:06 PM

探索前端中類似VSCode的面板拖拽調整功能的實現在前端開發中,如何實現類似於VSCode...

See all articles