當前位置:首頁 » 網頁前端 » 如何解決前端同步非同步問題
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

如何解決前端同步非同步問題

發布時間: 2023-05-27 17:12:28

⑴ js非同步問題怎麼解決

非同步載入又叫非阻塞載入,瀏覽器在下載執行js的同時,還會繼續進行後續頁面的處理。主要有三種方式。
方法一:也叫Script DOM Element
(function(){
var scriptEle = document.createElement("script");
scriptEle.type = "text/javasctipt";
scriptEle.async = true;
scriptEle.src = "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js";
var x = document.getElementsByTagName("head")[0];
x.insertBefore(scriptEle, x.firstChild);
})();

<async>屬性是HTML5中新增的非同步支持。此方法被稱為Script DOM Element 方法。Google Analytics 和 Google+ Badge 都使用了這種非同步載入代碼
(function(){;
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();

但是這種載入方式執行完之前會阻止onload事件的觸發,而現在很多頁面的代碼都在onload時還執行額外的渲染工作,所以還是會阻塞部分頁面的初始化處理。
方法二:onload時的非同步載入
(function(){
if(window.attachEvent){
window.attachEvent("load", asyncLoad);
}else{
window.addEventListener("load", asyncLoad);
}
var asyncLoad = function(){
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
}
)();

這種方法只是把插入script的方法放在一個函數裡面,然後放在window的onload方法裡面執行,這樣就解決了阻塞onload事件觸發的問題。
注:DOMContentLoaded與load的區別。前者是在document已經解析完成,頁面中的dom元素可用,但是頁面中的圖片,視頻,音頻等資源未載入完,作用同jQuery中的ready事件;後者的區別在於頁面所有資源全部載入完畢。
方法三:其他方法
由於JavaScript的動態性,還有很多非同步載入方法: XHR Injection、 XHR Eval、 Script In Iframe、 Script defer屬性、 document.write(script tag)。
XHR Injection(XHR 注入):通過XMLHttpRequest來獲取javascript,然後創建一個script元素插入到DOM結構中。ajax請求成功後設置script.text為請求成功後返回的responseText。
//獲取XMLHttpRequest對象,考慮兼容性。
var getXmlHttp = function(){
var obj;
if (window.XMLHttpRequest)
obj = new XMLHttpRequest();
else
obj = new ActiveXObject("Microsoft.XMLHTTP");
return obj;
};
//採用Http請求get方式;open()方法的第三個參數表示採用非同步(true)還是同步(false)處理
var xmlHttp = getXmlHttp();
xmlHttp.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true);
xmlHttp.send();
xmlHttp.onreadystatechange = function(){
if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
var script = document.createElement("script");
script.text = xmlHttp.responseText;
document.getElementsByTagName("head")[0].appendChild(script);
}
}

XHR Eval:與XHR Injection對responseText的執行方式不同,直接把responseText放在eval()函數裡面執行。
//獲取XMLHttpRequest對象,考慮兼容性。
var getXmlHttp = function(){
var obj;
if (window.XMLHttpRequest)
obj = new XMLHttpRequest();
else
obj = new ActiveXObject("Microsoft.XMLHTTP");
return obj;
};
//採用Http請求get方式;open()方法的第三個參數表示採用非同步(true)還是同步(false)處理
var xmlHttp = getXmlHttp();
xmlHttp.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true);
xmlHttp.send();
xmlHttp.onreadystatechange = function(){
if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
eval(xmlHttp.responseText);
//alert($);//可以彈出$,表明JS已經載入進來。click事件放在其它出會出問題,應該是還沒載入進來
$("#btn1").click(function(){
alert($(this).text());
});
}
}

Script In Irame:在父窗口插入一個iframe元素,然後再iframe中執行載入JS的操作。
var insertJS = function(){alert(2)};
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
var doc = iframe.contentWindow.document;//獲取iframe中的window要用contentWindow屬性。
doc.open();
doc.write("<script>var insertJS = function(){};<\/script><body onload='insertJS()'></body>");
doc.close();

GMail Mobile:業內JS內容被注釋,所以不會執行,在需要的時候,獲取script中的text內容去掉注釋,調用eval()執行。
<script type="text/javascript">
/*
var ...
*/
</script>

HTML5新屬性:async和defer屬性
defer屬性:IE4.0就出現。defer屬聲明腳本中將不會有document.write和dom修改。瀏覽器會並行下載其他有defer屬性的script。而不會阻塞頁面後續處理。註:所有的defer腳本必須保證按順序執行的。
<script type="text/javascript" defer></script>

async屬性:HTML5新屬性。腳本將在下載後盡快執行,作用同defer,但是不能保證腳本按順序執行。他們將在onload事件之前完成。
<script type="text/javascript" defer></script>

Firefox 3.6、Opera 10.5、IE 9和最新的Chrome和Safari都支持async屬性。可以同時使用async和defer,這樣IE 4之後的所有IE都支持非同步載入。
沒有async屬性,script將立即獲取(下載)並執行,期間阻塞了瀏覽器的後續處理。如果有async屬性,那麼script將被非同步下載並執行,同時瀏覽器繼續後續的處理。
總結: 對於支持HTML5的瀏覽器,實現JS的非同步載入只需要在script元素中加上async屬性,為了兼容老版本的IE還需加上defer屬性;對於不支持HTML5的瀏覽器(IE可以用defer實現),可以採用以上幾種方法實現。原理基本上都是向DOM中寫入script或者通過eval函數執行JS代碼,你可以把它放在匿名函數中執行,也可以在onload中執行,也可以通過XHR注入實現,也可以創建一個iframe元素,然後在iframe中執行插入JS代碼。

⑵ async/await 來處理非同步/同步

前面比較啰嗦,希望讀者耐心看完。
async/await可以說是一個老生常談,但是每一個前端工程師卻又避不開的話題。
在JQuery一把梭的時代,如果遇到下面類似的需求,你要怎麼做?
需求:拿到第一個ajax請求省,拿到省數據請求第二個ajax來獲得市的數據,拿到市的數據再請求第三個ajax來獲得區的數據。

最常見的寫法莫過於兩種:
1、回調地獄式寫法:

簡直令人發瘋。

或者是這樣?同步請求

我靠,只要一個數據請求不回來頁面就完犢子了。這簡直是噩夢。

別笑,我相信身為前端工程師一定這么寫過。

直到有一天,你發現vue里沒有Jquery,只有基於promise寫的axios.

一開始在使用axios的時候,還是脫離不了必須同步請求的變成思想,於是在vue項目中引入了jquery,把項目搞成了四不像。

研究了好久,終於發現了async/await

async 詞面翻譯 「非同步」。 async關鍵字一般放到function的前面,用來表示一個非同步函數。如下

根據缺敬冊阮一峰es6教程關於async部分的介紹,能得知async會返回一個Promise對象。所以我們可以通過then來調用。

如果不太明白,那我這么寫你一定會明白:

詞面意思「等待」,它只會在async函數體內出現,代表執行到這里的時候,等一下。它後面可以跟任何錶達式,不過一般會跟一個Promise,可以實現非同步函數同步調伏宏用的效果。

delay是非同步函數,在正常情況下,如果這樣寫:

會先輸出2,然後1秒後再輸出1.

但是在await的影響下,函數會先等待5秒後輸出a,在等待4秒後輸出b。

話題再轉回到我們一開始說的ajax,使用async/await的方法來寫,是不是感覺優雅多了?

在vue中,我們會使用axios來與後台做數據交互,axios沒稿前有同步請求,只有非同步請求,所以我們可以使用async/await來模擬一個同步請求,實現功能。