使用 Promise 達到同步等待 js 資源載入
前言
瀏覽器解析 HTML 是一行一行依序向下讀取,在傳統的寫法中,當瀏覽器讀到 <script> 時,便會暫停解析 DOM,開始下載 <script> 的資源 ,並在下載完成後立刻執行。由於這樣的特性,便可能造成在 DOM 樹建構不完全時就執行 JavaScript,其中需要操作 DOM 的程式可能就因此無法正確運作 ;又或者是在等待 <script> 資源下載、執行的過程中,使用者便會卡在白畫面。
解決的方法便是把 <script> 的位置都放到 <body> 的最後一行,來避免在 DOM 樹未建構完就開始執行程式的問題 但如果HTML,JavaScript檔案非常大,如果等到DOM載入完成後才開始下載,那讀取完成到可操作的期間會造成明顯的延遲。
Promise 建構式
Promise 建構函式建立同時,必須傳入一個函式作為參數(executor function),此函式的參數包含 resolve, reject, 這兩個方法分別代表成功與失敗的回傳結果,特別注意這兩個僅能回傳其中之一,回傳後表示此 Promise 事件結束。
new Promise(function(resolve, reject) {
resolve(); // 正確完成的回傳方法
reject(); // 失敗的回傳方法
});
結合script async
在HTML5新增的script async屬性,可用來幫助開發者控制 <script> 內資源的載入及執行順序,會在背景執行下載,當下載完成會馬上暫停 DOM 解析 ,並開始執行 JavaScript。也因為下載完成後會立即執行,加上 async 屬性後,就無法保證執行順序了,但可以透過屬性設定將它關閉。
let scripts = [`你的js路徑..`,`你的js路徑..`,...];
await Promise.all(scripts.map(async (url) => {
await loadScript(url);
}));
function loadScript(url) {
return new Promise(function (resolve, reject) {
let script = document.createElement('script');
script.src = url;
script.async = false; // 讓瀏覽器保持我們設定的 js 順序
// 判斷 JS 是否載入完成
script.onload = function () {
console.log('JS Loading succ:', url);
resolve(url);
};
script.onerror = function () {
console.log('JS Loading error:', url);
reject(url);
};
document.body.appendChild(script);
});
}