當前位置:首頁 » 編程軟體 » js腳本非同步問題

js腳本非同步問題

發布時間: 2022-11-26 09:33:37

『壹』 我對JS非同步執行機制的理解

說起JS的非同步執行機制,如果網路一下,你首先會發現阮一峰的寫過一篇關於非同步機制的文章( http://www.ruanyifeng.com/blog/2014/10/event-loop.html ),等你津津有味又一頭霧水的看完,然後繼續看網路的其他結果,然後會發現,阮一峰的這篇被另一個大牛朴靈給批判了
( http://www.360doc.com/content/14/1011/13/15077656_416048738.shtml )。

由此可見,關於非同步執行機制到底是怎麼回事,因為涉及到瀏覽器底層機制,所以不容易徹底了解清楚,就算是大牛阮一峰,也只是通過英文文獻來了解,而且一知半解。我的這篇文章只是試圖盡可能簡單的描述一下JS的非同步執行機制,坦白說,我現在並不能完全弄懂這個機制,所以也不能完全解釋清這個機制,所以,如果我寫的越嚴謹,就越容易出錯,我只能簡單但是較模糊的描述一下:

JS的運行環境是一個很復雜的環境,它裡面有非常多的復雜的名詞事物,用簡單又不嚴謹的說法來說,運行環境里至少有下面這些事物:

有一個國外的web app,專門用來講解非同步事件的門道 Loupe ,這個更接近真實情況。為什麼我不講解這個?因為更復雜了,我們並不打算研究瀏覽器的底層,不是么?

然後說一下任務隊列里的任務。所有任務可以分成兩種,一種是同步任務(synchronous),另一種是非同步任務(asynchronous)。同步任務指的是,靠主線程自己就可以執行完成的任務;非同步任務指的是,主線程執行開始之後,需要靠主線程之外的線程才能完成的任務。由主線程決定是否要動用其他線程。以下內容,不再提棧,只說主線程。

現在說重點:

非同步任務的執行機制是:

當主線程遇到一個非同步任務,比如一個ajax請求,當主線程執行到 xhr.send() 的時候,這個send命令是立即執行的, 並不會像一些人想像的,拖到所有同步任務的最後面。 然後主線程向http線程發送指令,要求http線程向伺服器發送請求。這里強調一下http線程,顯然它不是主線程的一部分,因為它可以並發,如果你有100個ajax請求,每個都需要1秒鍾,是不是http線程要花100秒呢?並不是,它會並發100個請求,總共耗時大約1.01秒就完成了。

主線程向以http線程為代表的幾個線程發送指令之後,主線程就暫時不再管這個ajax任務了,而是去看任務隊列里的下一個任務。

http線程發送了請求之後接收反饋,收到之後,形成一個新的事件(可以叫做「我收到啦!」事件),然後插入到回調函數隊列中,因為回調函數隊列的優先順序很低,所以會排到總隊列的最後面,其結果就是:主線程把同步任務都完成了,才開始執行非同步事件的 回調 注意,並不是非同步任務在全體同步任務結束之後才開始,而是非同步任務的回調通常在全體同步任務結束之後才開始!非同步任務跟非同步任務的回調是兩回事!是兩個任務!一個鮮明的例子就是 setTimeout(fn, 1000) ,計時是從主線程遇到 setTimeout() 任務,然後分配給計時器線程,計時器線程開始幹活的時候就開始計時了!只不過要1秒之後 fn 才執行! setTimeout() 和 fn 是兩個任務! setTimeout() 是立即執行, fn 才是1秒之後執行。但是 setTimeout() 的執行,人眼是感受不到的,因為並沒有什麼地方有一個秒錶告訴你 setTimeout() 開始執行了;而fn的執行,人眼能感受到,所以人們會錯誤的以為fn才是非同步任務,其實fn並不是, fn 是個回調任務,往往 fn 是同步任務,比如 fn 可能是 console.log(123) ,這怎麼會是非同步任務。

所以,非同步機制是瀏覽器的兩個或以上常駐線程共同完成的,非同步請求是JS主線程和其他某個線程共同完成的,JS的執行線程發起非同步請求(這時瀏覽器會開一條新的HTTP請求線程來執行請求,這時JS自己的任務已完成,繼續執行線程隊列中剩下的其他任務),然後在未來的某一時刻"任務隊列"線程監視到之前的發起的HTTP請求已完成, "任務隊列"就會把完成事件插入到JS執行隊列的尾部等待JS處理

最後專門說說定時觸發(settimeout和setinterval)。

定時觸發是由瀏覽器的定時器線程執行的定時計數, 然後在定時時間到達之後,定時器線程把定時處理函數的執行請求插入到JS回調隊列的尾端。

這個1到底是100毫秒之後彈出,還是1000毫秒(或更多時間)後彈出呢?又或是1100毫秒之後彈出?

答案是,1000毫秒多一點點之後彈出。

原因:瀏覽器會先執行setTimeout,也就是開始計時,然後開始執行sometask,執行了1000毫秒,然後去回調隊列里看回調任務,alert(1);早就恭候了,因為定時100毫秒之後alert(1)就可以執行了。所以,等1000毫秒的任務完成,1就會立即彈出,所以答案是1000毫秒多一點點之後彈出。

所以用這兩個函數的時候,實際的執行時間是大於或等於指定時間的,不保證能准確定時的。

最後強調一下setInterval。比如我希望每100毫秒列印一個1。然後,又有極端情況,就是sometask耗時1000毫秒。你以為sometask結束之後會打出10個1么?並不會,只會打出1個1,因為setInterval第一次讀秒結束之後,回調隊列出現了一個alert(1),根據之前的理論,並不會執行。又過了100毫秒之後,計時器線程會去觀察回調隊列是不是已經有了alert(1),如果有,就不再往回調隊列里加alert(1),目的就是為了避免回調疊加執行。

總之,你需要記住,非同步任務就是主線程在任務隊列里看到了這個任務,看了一眼之後就然後安排別的線程幫忙,然後主線程就忙別的去了,別的線程幫忙完事之後,再在隊列末尾放一個新任務叫「幫忙完畢」,到此非同步任務本身就完事。主任務看到「幫忙完畢」任務之後,就去執行回調,回調執行完,這個非同步任務連同回調就全都完事。然後,如果並沒有回調。。。沒有就沒有唄。

『貳』 JS非同步操作新體驗之 async函數

文章來自 https://www.cnblogs.com/rogerwu/p/10784236.html

async 函數返回一個 Promise 實例對象,可以使用 then 方法添加回調函數。

當函數執行時,一旦遇到 await 就會先返回,等到非同步操作完成,再接著執行函數體內後面的語句

(1)、async 函數內部 return語句返回的值,會成為then方法回調函數的參數

(2)、async 函數內部拋出錯誤,會導致返回的 Promise對象變成reject狀態,拋出的錯誤會被catch方法回調函數接收到

(3)、只有 async 函數內部的非同步操作執行完,才會執行 then方法指定的回調函數

實際應用

『叄』 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);
})();
方法二: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);
}
})();

方法三:$(document).ready()

『肆』 js執行順序+同步非同步

按照js同步執行的順序,函數調用會首先執行for循環,循環5次開啟了5個延遲器,延時器內部的回調函數將會非同步執行,會在延時1s後進入消息隊列等待執行。循環了5次,所以此時i的值為5,然後同步執行console.log列印5,第一次同步執行結束,然後開始執行消息隊列的非同步任務,列印5個5,中間的undefined是函數調用無返回值返回的。

執行順序和第一題相同,不同的是延時器被包裹在了一個立即執行函數內容,並把每一次循環的i作為參數傳入,此時循環內部的函數形成了一個私有作用域,每一次的i變數被獨立保存了起來,因此每一次循環的i的值都會被列印出來。

延時器內部回調函數是非同步函數,將在延時結束後,進入消息隊列等待執行,因此同步的console.log("CCCC")最優先執行,然後延時0ms的延時器的回調先進入消息隊列,1000ms後第一個延時器的回調再進入消息隊列等待執行,因此先執行0ms的回調列印DDDD,再執行1000ms的回調列印BBBB。

這里與上一題不同的是中間多了一個執行3s的同步while循環,因此執行順序是這樣的:
第一個延時器延時1s後內部非同步回調函數進入消息隊列等待執行,
等待while循環3s後列印"CCCC",
循環結束後第一個延時器內部的回調已經進入消息隊列第一個執行位置等待執行。
第二個延時器延時0s後內部非同步回調函數進入消息隊列等待執行,延時結束後排到第一個延時器的回調函數後面,
因此非同步隊里內部先列印"BBBB",再列印"DDDD"

『伍』 javascript中非同步操作的異常怎麼處理

一、JavaScript非同步編程的兩個核心難點
非同步I/O、事件驅動使得單線程的JavaScript得以在不阻塞UI的情況下執行網路、文件訪問功能,且使之在後端實現了較高的性能。然而非同步風格也引來了一些麻煩,其中比較核心的問題是:
1、函數嵌套過深
JavaScript的非同步調用基於回調函數,當多個非同步事務多級依賴時,回調函數會形成多級的嵌套,代碼變成

金字塔型結構。這不僅使得代碼變難看難懂,更使得調試、重構的過程充滿風險。
2、異常處理
回調嵌套不僅僅是使代碼變得雜亂,也使得錯誤處理更復雜。這里主要講講異常處理。
二、異常處理
像很多時髦的語言一樣,JavaScript 也允許拋出異常,隨後再用一個try/catch
語句塊捕獲。如果拋出的異常未被捕獲,大多數JavaScript環境都會提供一個有用的堆棧軌跡。舉個例子,下面這段代碼由於'{'為無效JSON
對象而拋出異常。
?

12345678

function JSONToObject(jsonStr) { return JSON.parse(jsonStr);}var obj = JSONToObject('{');//SyntaxError: Unexpected end of input//at Object.parse (native)//at JSONToObject (/AsyncJS/stackTrace.js:2:15)//at Object.<anonymous> (/AsyncJS/stackTrace.js:4:11)

堆棧軌跡不僅告訴我們哪裡拋出了錯誤,而且說明了最初出錯的地方:第4 行代碼。遺憾的是,自頂向下地跟蹤非同步錯誤起源並不都這么直截了當。
非同步編程中可能拋出錯誤的情況有兩種:回調函數錯誤、非同步函數錯誤。
1、回調函數錯誤
如果從非同步回調中拋出錯誤,會發生什麼事?讓我們先來做個測試。
?

1234567

setTimeout(function A() { setTimeout(function B() { setTimeout(function C() { throw new Error('Something terrible has happened!'); }, 0); }, 0);}, 0);

上述應用的結果是一條極其簡短的堆棧軌跡。
?

12

Error: Something terrible has happened!at Timer.C (/AsyncJS/nestedErrors.js:4:13)

等等,A 和B 發生了什麼事?為什麼它們沒有出現在堆棧軌跡中?這是因為運行C 的時候,非同步函數的上下文已經不存在了,A 和B 並不在內存堆棧里。這3
個函數都是從事件隊列直接運行的。基於同樣的理由,利用try/catch
語句塊並不能捕獲從非同步回調中拋出的錯誤。另外回調函數中的return也失去了意義。
?

1234567

try { setTimeout(function() { throw new Error('Catch me if you can!'); }, 0);} catch (e) {console.error(e);}

看到這里的問題了嗎?這里的try/catch 語句塊只捕獲setTimeout函數自身內部發生的那些錯誤。因為setTimeout
非同步地運行其回調,所以即使延時設置為0,回調拋出的錯誤也會直接流向應用程序。
總的來說,取用非同步回調的函數即使包裝上try/catch 語句塊,也只是無用之舉。(特例是,該非同步函數確實是在同步地做某些事且容易出錯。例如,Node
的fs.watch(file,callback)就是這樣一個函數,它在目標文件不存在時會拋出一個錯誤。)正因為此,Node.js
中的回調幾乎總是接受一個錯誤作為其首個參數,這樣就允許回調自己來決定如何處理這個錯誤。
2、非同步函數錯誤
由於非同步函數是立刻返回的,非同步事務中發生的錯誤是無法通過try-catch來捕捉的,只能採用由調用方提供錯誤處理回調的方案來解決。
例如Node中常見的function (err, ...)
{...}回調函數,就是Node中處理錯誤的約定:即將錯誤作為回調函數的第一個實參返回。再比如HTML5中FileReader對象的onerror函數,會被用於處理非同步讀取文件過程中的錯誤。
舉個例子,下面這個Node 應用嘗試非同步地讀取一個文件,還負責記錄下任何錯誤(如「文件不存在」)。
?

1234567

var fs = require('fs'); fs.readFile('fhgwgdz.txt', function(err, data) { if (err) { return console.error(err); }; console.log(data.toString('utf8'));});

客戶端JavaScript 庫的一致性要稍微差些,不過最常見的模式是,針對成敗這兩種情形各規定一個單獨的回調。jQuery 的Ajax
方法就遵循了這個模式。
?

1234

$.get('/data', { success: successHandler, failure: failureHandler});

不管API 形態像什麼,始終要記住的是,只能在回調內部處理源於回調的非同步錯誤。
三、未捕獲異常的處理
如果是從回調中拋出異常的,則由那個調用了回調的人負責捕獲該異常。但如果異常從未被捕獲,又會怎麼樣?這時,不同的JavaScript環境有著不同的游戲規則……
1. 在瀏覽器環境中
現代瀏覽器會在開發人員控制台顯示那些未捕獲的異常,接著返回事件隊列。要想修改這種行為,可以給window.onerror
附加一個處理器。如果windows.onerror 處理器返回true,則能阻止瀏覽器的默認錯誤處理行為。
?

123

window.onerror = function(err) { return true; //徹底忽略所有錯誤};

在成品應用中, 會考慮某種JavaScript 錯誤處理服務, 譬如Errorception。Errorception
提供了一個現成的windows.onerror 處理器,它向應用伺服器報告所有未捕獲的異常,接著應用伺服器發送消息通知我們。
2. 在Node.js 環境中
在Node 環境中,window.onerror 的類似物就是process 對象的uncaughtException 事件。正常情況下,Node
應用會因未捕獲的異常而立即退出。但只要至少還有一個uncaughtException 事件處理
器,Node 應用就會直接返回事件隊列。
?

123

process.on('uncaughtException', function(err) { console.error(err); //避免了關停的命運!});

但是,自Node 0.8.4 起,uncaughtException 事件就被廢棄了。據其文檔所言,對異常處理而言,uncaughtException
是一種非常粗暴的機制,請勿使用uncaughtException,而應使用Domain 對象。
Domain 對象又是什麼?你可能會這樣問。Domain 對象是事件化對象,它將throw 轉化為'error'事件。下面是一個例子。
?

123456789

var myDomain = require('domain').create();myDomain.run(function() { setTimeout(function() { throw new Error('Listen to me!') }, 50);});myDomain.on('error', function(err) { console.log('Error ignored!');});

源於延時事件的throw 只是簡單地觸發了Domain 對象的錯誤處理器。
Error ignored!
很奇妙,是不是?Domain 對象讓throw
語句生動了很多。不管在瀏覽器端還是伺服器端,全局的異常處理器都應被視作最後一根救命稻草。請僅在調試時才使用它。
四、幾種解決方案
下面對幾種解決方案的討論主要集中於上面提到的兩個核心問題上,當然也會考慮其他方面的因素來評判其優缺點。
1、Async.js
首先是Node中非常著名的Async.js,這個庫能夠在Node中展露頭角,恐怕也得歸功於Node統一的錯誤處理約定。

而在前端,一開始並沒有形成這么統一的約定,因此使用Async.js的話可能需要對現有的庫進行封裝。
Async.js的其實就是給回調函數的幾種常見使用模式加了一層包裝。比如我們需要三個前後依賴的非同步操作,採用純回調函數寫法如下:
?

12345678910111213141516

asyncOpA(a, b, (err, result) => { if (err) { handleErrorA(err); } asyncOpB(c, result, (err, result) => { if (err) { handleErrorB(err); } asyncOpB(d, result, (err, result) => { if (err) { handlerErrorC(err); } finalOp(result); }); });});

如果我們採用async庫來做:
?async.waterfall([ (cb) => { asyncOpA(a, b, (err, result) => { cb(err, c, result); }); }, (c, lastResult, cb) => { asyncOpB(c, lastResult, (err, result) => { cb(err, d, result); }) }, (d, lastResult, cb) => { asyncOpC(d, lastResult, (err, result) => { cb(err, result); }); }], (err, finalResult) => { if (err) { handlerError(err); } finalOp(finalResult);});

可以看到,回調函數由原來的橫向發展轉變為縱向發展,同時錯誤被統一傳遞到最後的處理函數中。

其原理是,將函數數組中的後一個函數包裝後作為前一個函數的末參數cb傳入,同時要求:
每一個函數都應當執行其cb參數;cb的第一個參數用來傳遞錯誤。我們可以自己寫一個async.waterfall的實現:
?let async = { waterfall: (methods, finalCb = _emptyFunction) => { if (!_isArray(methods)) { return finalCb(new Error('First argument to waterfall must be an array of functions')); } if (!methods.length) { return finalCb(); } function wrap(n) { if (n === methods.length) { return finalCb; } return function (err, ...args) { if (err) { return finalCb(err); } methods[n](...args, wrap(n + 1)); } } wrap(0)(false); }};

Async.js還有series/parallel/whilst等多種流程式控制制方法,來實現常見的非同步協作。
Async.js的問題:
在外在上依然沒有擺脫回調函數,只是將其從橫向發展變為縱向,還是需要程序員熟練非同步回調風格。

錯誤處理上仍然沒有利用上try-catch和throw,依賴於「回調函數的第一個參數用來傳遞錯誤」這樣的一個約定。
2、Promise方案
ES6的Promise來源於Promise/A+。使用Promise來進行非同步流程式控制制,有幾個需要注意的問題,

把前面提到的功能用Promise來實現,需要先包裝非同步函數,使之能返回一個Promise:
?

12345678910

function toPromiseStyle(fn) { return (...args) => { return new Promise((resolve, reject) => { fn(...args, (err, result) => { if (err) reject(err); resolve(result); }) }); };}

這個函數可以把符合下述規則的非同步函數轉換為返回Promise的函數:
回調函數的第一個參數用於傳遞錯誤,第二個參數用於傳遞正常的結果。接著就可以進行操作了:
?

123456789101112131415

let [opA, opB, opC] = [asyncOpA, asyncOpB, asyncOpC].map((fn) => toPromiseStyle(fn)); opA(a, b) .then((res) => { return opB(c, res); }) .then((res) => { return opC(d, res); }) .then((res) => { return finalOp(res); }) .catch((err) => { handleError(err); });

通過Promise,原來明顯的非同步回調函數風格顯得更像同步編程風格,我們只需要使用then方法將結果傳遞下去即可,同時return也有了相應的意義:

在每一個then的onFullfilled函數(以及onRejected)里的return,都會為下一個then的onFullfilled函數(以及onRejected)的參數設定好值。
如此一來,return、try-catch/throw都可以使用了,但catch是以方法的形式出現,還是不盡如人意。
3、Generator方案
ES6引入的Generator可以理解為可在運行中轉移控制權給其他代碼,並在需要的時候返回繼續執行的函數。利用Generator可以實現協程的功能。
將Generator與Promise結合,可以進一步將非同步代碼轉化為同步風格:
?

1234567891011

function* getResult() { let res, a, b, c, d; try { res = yield opA(a, b); res = yield opB(c, res); res = yield opC(d); return res; } catch (err) { return handleError(err); }}

然而我們還需要一個可以自動運行Generator的函數:
?

2324252627282930

function spawn(genF, ...args) { return new Promise((resolve, reject) => { let gen = genF(...args); function next(fn) { try { let r = fn(); if (r.done) { resolve(r.value); } Promise.resolve(r.value) .then((v) => { next(() => { return gen.next(v); }); }).catch((err) => { next(() => { return gen.throw(err); }) }); } catch (err) { reject(err); } } next(() => { return gen.next(undefined); }); });}

用這個函數來調用Generator即可:
?

1234567

spawn(getResult) .then((res) => { finalOp(res); }) .catch((err) => { handleFinalOpError(err); });

可見try-catch和return實際上已經以其原本面貌回到了代碼中,在代碼形式上也已經看不到非同步風格的痕跡。
類似的功能有co/task.js等庫實現。
4、ES7的async/await
ES7中將會引入async function和await關鍵字,利用這個功能,我們可以輕松寫出同步風格的代碼,

同時依然可以利用原有的非同步I/O機制。
採用async function,我們可以將之前的代碼寫成這樣:
?

12345678910111213

async function getResult() { let res, a, b, c, d; try { res = await opA(a, b); res = await opB(c, res); res = await opC(d); return res; } catch (err) { return handleError(err); }} getResult();

和Generator & Promise方案看起來沒有太大區別,只是關鍵字換了換。
實際上async
function就是對Generator方案的一個官方認可,將之作為語言內置功能。
async function的缺點:
await只能在async function內部使用,因此一旦你寫了幾個async function,或者使用了依賴於async
function的庫,那你很可能會需要更多的async function。
目前處於提案階段的async
function還沒有得到任何瀏覽器或Node.JS/io.js的支持。Babel轉碼器也需要打開實驗選項,並且對於不支持Generator的瀏覽器來說,還需要引進一層厚厚的regenerator
runtime,想在前端生產環境得到應用還需要時間。
以上就是本文的全部內容,希望對大家的學習有所幫助。

『陸』 js單線程和js非同步操作的幾種方法

為了解決這個問題,Javascript語言將任務的執行模式分成兩種:同步(Synchronous)和非同步(Asynchronous)。
"同步模式"就是上一段的模式,後一個任務等待前一個任務結束,然後再執行,程序的執行順序與任務的排列順序是一致的、同步的;"非同步模式"則完全不同,每一個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,所以程序的執行順序與任務的排列順序是不一致的、非同步的。
"非同步模式"非常重要。在瀏覽器端,耗時很長的操作都應該非同步執行,避免瀏覽器失去響應,最好的例子就是Ajax操作。在伺服器端,"非同步模式"甚至是唯一的模式,因為執行環境是單線程的,如果允許同步執行所有http請求,伺服器性能會急劇下降,很快就會失去響應。

『柒』 js 如何判斷是非同步請求還是普通請求

這是根據請求時的參數來決定的啊,如果async為true就是非同步請求,為false就是同步請求。也就是說,是否非同步請求是由前端決定的,後台程序是不作區分一視同仁處理的。前端如果是以同步方式發出請求,它就會阻塞程序,等待後台返回數據再繼續運行;而非同步方式的話,就會立刻返回,繼續執行其他代碼,當後台返回數據時再以回調函數的形式進行處理。既然同步非同步是由前端決定的,那麼前端的js自然就知道如何來處理這個請求結果啦。

『捌』 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代碼。

『玖』 js/javascript 非同步執行方法

var xmlHttp;
function createXMLHttpRequest(){
//Mozilla 瀏覽器(將XMLHttpRequest對象作為本地瀏覽器對象來創建)
if(window.XMLHttpRequest){ //Mozilla 瀏覽器
xmlHttp = new XMLHttpRequest();
}else if(window.ActiveXObject) { //IE瀏覽器
//IE瀏覽器(將XMLHttpRequest對象作為ActiveX對象來創建)
try{
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}catch(e){}
}
}
if(xmlHttp == null){
alert("不能創建XMLHttpRequest對象");
return false;
}
}
//用於發出非同步請求的方法
function sendAsynchronRequest(url,parameter,callback){
createXMLHttpRequest();
if(parameter == null){
//設置一個事件處理器,當XMLHttp狀態發生變化,就會出發該事件處理器,由他調用
//callback指定的javascript函數
xmlHttp.onreadystatechange = callback;
//設置對拂去其調用的參數(提交的方式,請求的的url,請求的類型(非同步請求))
xmlHttp.open("GET",url,true);//true表示發出一個非同步的請求。
xmlHttp.send(null);
}else{
xmlHttp.onreadystatechange = callback;
xmlHttp.open("POST",url,true);
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");
xmlHttp.send(parameter);
}
}
//以上代碼是通用的方法,接下來是調用以上的方法
function loadPros(title,count,pid,cid,level){
// 調用非同步請求方法
url = "。。。。。。。。";
sendAsynchronRequest(url,null,loadCallBack);
}
// 指定回調方法
function loadCallBack(){
try
{
if (xmlHttp.readyState == 4) {
if (xmlHttp.status == 200) {
if(xmlHttp.responseText != null && xmlHttp.responseText != ""){
var divProid = document.getElementById('videolist');
divProid.innerHTML = xmlHttp.responseText;
for(i=0;i<len;i++)
{
var video_url = document.getElementById("videolist"+i+"").href;
if(video_url != undefined && video_url != null && video_url != ""){
window.location.href = video_url;
}
}
}
}
}
if (xmlHttp.readyState == 1)
{
//alert("正在載入連接對象......");
}
if (xmlHttp.readyState == 2)
{
//alert("連接對象載入完畢。");
}
if (xmlHttp.readyState == 3)
{
//alert("數據獲取中......");
}
}
catch (e)
{
//alert(e);
}
}

『拾』 javascript腳本如何非同步載入,有什麼作用

關於JavaScript腳本載入的問題,相信大家碰到很多。主要在幾個點——
1> 同步腳本和非同步腳本帶來的文件載入、文件依賴及執行順序問題

2> 同步腳本和非同步腳本帶來的性能優化問題
深入理解腳本載入相關的方方面面問題,不僅利於解決實際問題,更加利於對性能優化的把握並執行。
先看隨便一個script標簽代碼——
復制代碼代碼如下:

<script src="js/myApp.js"></script>

如果放在<head>上面,會阻塞所有頁面渲染工作,使得用戶在腳本載入完畢並執行完畢之前一直處於「白屏死機」狀態。而<body>末尾的打腳本只會讓用戶看到毫無生命力的靜態頁面,原本應該進行客戶端渲染的地方卻散布著不起作用的控制項和空空如也的方框。拿一個測試用例——

代碼如下:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>非同步載入script</title>
<script src="js/test.js"></script>
</head>
<body>
<div>我是內容</div>
<img src="img/test.jpg">
</body>
</html>

其中,test.js中的內容——

代碼如下:
alert('我是head裡面的腳本代碼,執行這里的js之後,才開始進行body的內容渲染!');

我們會看到,alert是一個暫停點,此時,頁面是空白的。但是要注意,此時整個頁面已經載入完畢,如果body中包含某些src屬性的標簽(如上面的img標簽),此時瀏覽器已經開始載入相關內容了。總之要注意——js引擎和渲染引擎的工作時機是互斥的(一些書上叫它為UI線程)。

因此,我們需要——那些負責讓頁面更好看、更好用的腳本應該立即載入,而那些可以待會兒再載入的腳本稍後再載入。

熱點內容
魔獸世界自動釣魚腳本 發布:2024-05-19 06:43:07 瀏覽:494
cbs加密 發布:2024-05-19 06:29:56 瀏覽:200
ssis存儲過程 發布:2024-05-19 06:21:31 瀏覽:630
怎樣刪除小視頻文件夾 發布:2024-05-19 05:49:29 瀏覽:589
開啟php短標簽 發布:2024-05-19 05:44:12 瀏覽:473
android各國語言 發布:2024-05-19 05:42:54 瀏覽:247
微信什麼資料都沒怎麼找回密碼 發布:2024-05-19 05:35:34 瀏覽:907
填志願密碼是什麼 發布:2024-05-19 05:30:23 瀏覽:318
城堡爭霸自動掠奪腳本 發布:2024-05-19 05:22:06 瀏覽:204
asp編程工具 發布:2024-05-19 05:20:36 瀏覽:143