摘要:異步與同步技術研究概念介紹異步簡寫同步簡寫用比方來比喻異步就是個人同時起跑起點和出發時間相同在起跑時不去關心其他人會啥時候跑完尼瑪這不廢話嗎大家都才起跑怎么知道別人多就跑完同步就是個人接力跑起點和出發時間不同且后一個人會等待前一個人跑完才能
1. 異步與同步 技術研究 (1). 概念介紹
異步: asynchronous 簡寫async
同步: synchronous 簡寫sync
用比方來比喻
異步就是: N個人同時起跑, 起點和出發時間相同, 在起跑時不去關心其他人會啥時候跑完~尼瑪這不廢話嗎?大家都才起跑怎么知道別人多就跑完.
同步就是: N個人接力跑, 起點和出發時間不同, 且后一個人會等待前一個人跑完才能繼續跑, 也就是要關心前一個人的結果(上一行代碼的返回值).
JS運行場景多數是在用戶瀏覽器上, 程序效率優劣會直接影響用戶的體驗交互. 比如一個網站, 在用戶注冊時, 會ajax校驗輸入再發提交表單, 如果用同步就可能會一直卡著等待ajax響應, 好幾秒結束后再跳到注冊結果頁, 這個體驗將是非常糟糕的.
說到JS的異步, 不得不提及一個非常有代表意義函數了.
JavaScriptvar url = "/action/"; var data = "i=1"; xmlHTTP = new XMLHttpRequest(); xmlHTTP.nonce = nonce; xmlHTTP.open("POST", url); xmlHTTP.onreadystatechange = function(a) { if(a.target.readyState!=4)return false; try{ console.log(a.target.responseText) }catch(e){ return false; } }; xmlHTTP.send(data);
或者在jQuery寫作:
JavaScript$.ajax({ url: "/action/", type: "POST", data: "i=1", success: function(responseText){ console.log(responseText); } })
上面的無論是xmlHTTP.onreadystatechange, 還是success, 在JavaScript中均稱為回調方法,
以原生JS的XMLHttpRequest為例, xmlHTTP變量是個XMLHttpRequest對象, 他的onreadystatechange是在每次請求響應狀態發生變化時會觸發的一個函數/方法, 然后在發出請求xmlHTTP.send(data)的時候, JS并不會理會onreadystatechange方法, 而當改送請求到達服務器, 開始響應或者響應狀態改變時會調用onreadystatechange方法:
也就是
1) 請求發出
2) 服務器開始響應數據
3) 執行回調方法, 可能執行多次
以jQuery版為例, $.ajax本身是個函數, 唯一一個參數是{...} 這個對象, 然后回調方法success是作為這個對象的一個屬性傳入$.ajax的.
$.ajax()先將數據post到"/action/", 返回結果后再調用success(如果發生錯誤會調用error).
也就是
1) 請求發出 2) 服務器開始響應數據 3) 響應結束執行回調方法
然后作為函數$.ajax, 是函數就應該有返回值(哪怕沒有return也會返回undefined), 他本身的返回值是多少呢?
分為async:true和async:false兩個版本:
async:true版本:
JavaScript$.ajax({"url":"a.html", type:"GET", async:true}) > Object {readyState: 1}
async:false版本:
JavaScript$.ajax({"url":"robots.txt", type:"GET", false}) > Object {readyState: 4, responseText: "我們可以直接看到, async:true異步模式下, jquery/javascript未將結果返回... 而async:false就將結果返回了.
然后問題就來了, 為什么async:true未返回結果呢?
答案很簡單:
因為在返回的時候, 程序不可能知道結果. 異步就是指不用等此操作執行出結果再往下執行, 也就是返回的值中未包含結果.留下一個問題, 我們是不是為了程序流程的簡單化而使用同步呢?
(3). 異步的困惑先帖一段代碼:
a.phpphppage.js
JavaScriptfor( i = 1; i <= 4; i++ ){ $.ajax({ url: "a.php", type: "POST", dataType: "json", data: {data: i}, async: true, // 默認即為異步 success: function(json) { console.log(i + ": " + json); // 打印 } }); }你們猜猜打印的那行會最終打印出什么內容?
是1: {} 2: {} 3: {} 4: {}嗎?
錯!
輸出的將是:
4: {} 4: {} 4: {} 4: {}你TM在逗我?
沒有, 這并不是JS的BUG, 也不是jQuery的BUG.
這是因為, PHP休息了一秒, 而js異步地循環從1到4, 遠遠用不到1秒.
然后在1秒鐘后, 才開始返回數據, 觸發success, 此時此刻i已經自增成了4.
自然而然地, 第一次console.log(i...)就是4, 第二次也是, 第三次也是, 第四次也是.
那么如果我們希望程序輸出也1,2,3,4這樣輸出怎么辦呢?兩種方案:
1) 讓后端輸出i
a.php
phppage.js
JavaScriptfor( i = 1; i <= 4; i++ ){ $.ajax({ url: "a.php", type: "POST", dataType: "json", data: {data: i}, async: true, success: function(json) { console.log(json.i + ": " + json); // 這一行改了 } }); }2) 給回調的事件對象賦屬性
a.php
php保持原代碼不變page.js
JavaScriptfor( i = 1; i <= 4; i++ ){ ajaxObj = $.ajax({ // 將ajax賦給ajaxObj url: "a.php", type: "POST", dataType: "json", data: {data: i}, async: true, success: function(json, status, obj) { // 增加回調參數, jQuery文檔有說第三個參數就是ajax方法產生的對象. console.log(obj.i + ": " + json); // 從jQuery.ajax返回的對象中取i } }); ajaxObj.i = i; // 給ajaxObj賦屬性i 值為循環的i }有可能你會感到困惑, 為何可以給ajaxObj設置一個i屬性然后在回調時用第三個回調參數的i屬性呢?jQuery.ajax文檔中寫到:
jQuery.ajax( [settings ] ) settings ... success: Function( Anything data, String textStatus, jqXHR jqXHR ) 第1個參數就是響應的文本/HTML/XML/數據/json之類的, 跟你的dataType設置有關 第2個參數就是status狀態, 如success 第3個參數就是jqXHR, 也就是jQuery的XMLHttpRequest對象, 當然, 在這里就是$.ajax()生成的對象, 也就是事件的觸發者本身, 給本身設置一個屬性(ajaxObj.i = i), 然后再調用本身的回調時, 使用本身的那個屬性(obj.i), 當然會保持一致了.然后
1)輸出的結果將是1: {i:1} 2: {i:2} 3: {i:3} 4: {i:4}2)輸出的結果將是
1: {} 2: {} 3: {} 4: {}雖然略有區別, 但兩者均可達到要求. 若要論代碼的逼格, 相信你一定會被第二個方案給震驚的.
憑什么你給ajaxObj賦個屬性就可以在success中用了呢?請看(4). 異步的回調機制
(4). 異步的回調機制 ------ 事件一個有經驗的JavaScript程序員一定會將js回調用得得心應手.
因為JavaScript天生異步, 異步的好處是顧及了用戶的體驗, 但壞處就是導致流程化循環或者遞歸的邏輯明明在別的語言中無任何問題, 卻在js中無法取得期待的值...
而JavaScript異步在設計之初就將這一點考慮到了. 任何流行起來的JS插件方法, 如jQuery的插件, 一定考慮到了這一點了的.舉個例子.
ajaxfileupload插件, 實現原理是將選擇的文件$.clone到一個form中, form的target設置成了一個頁面中的iframe, 然后定時取iframe的contents().body, 即可獲得響應的值. 如果要支持multiple文件上傳(一些現代化的瀏覽器支持), 還是得要用`XMLHttpRequest`如下面代碼:
$("input#file").on("change", function(e){ for(i = 0; i < e.target.files.length; i++ ){ var data = new FormData(); data.append("file", e.target.files[i]); xmlHTTP = new XMLHttpRequest(); xmlHTTP.open("POST", s.url); xmlHTTP.onreadystatechange = function(a) { // a 為 事件event對象 if(a.target.readyState!=4)return false; // a.target為觸發這個事件的對象 即xmlHTTP (XMLHttpRequest) 對象 try{ console.log(a.target.responseText); }catch(e){ return false; } }; xmlHTTP.send(data); } })你可以很明顯地知道, 在onreadystatechange調用且走到console.log(a.target.responseText)時, 如果服務器不返回文件名, 我們根本并不知道返回的是哪個文件的URL. 如果根據i去取的話, 那么很容易地, 我們只會取到始終1個或幾個, 并不能保證準確.
那么我們應該怎么去保證在console.log(a.target.responseText)時能知道我信上傳的文件的基本信息呢?$("input#file").on("change", function(e){ for(i = 0; i < e.target.files.length; i++ ){ var data = new FormData(); data.append("file", e.target.files[i]); xmlHTTP = new XMLHttpRequest(); xmlHTTP.file = e.target.files[i]; xmlHTTP.open("POST", s.url); xmlHTTP.onreadystatechange = function(a) { if(a.target.readyState!=4)return false; try{ console.log(a.target.file); //這兒是上面`xmlHTTP.file = e.target.files[i]` 賦進去的 console.log(a.target.responseText); }catch(e){ return false; } }; xmlHTTP.send(data); } })是不是很簡單?
2. 展望 (1). Google對同步JavaScript的態度在你嘗試在chrome打開的頁面中執行async: false的代碼時, chrome將會警告你:
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user"s experience. For more help, check http://xhr.spec.whatwg.org/.(2). 職場展望異步和事件將是JavaScript工程師必備技能
[完]
Reference:1.《Javascript異步編程的4種方法》 http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html 2.《什么是 Event Loop?》 http://www.ruanyifeng.com/blog/2013/10/event_loop.html 3.《JavaScript 運行機制詳解:再談Event Loop》 http://www.ruanyifeng.com/blog/2014/10/event-loop.html補充:
異步的數據一致性問題也可以用v1兄提供的方法來解決:for(i=1; i < 4; i++){ (function(i){ $.ajax({ url: "URL", type: "get", dataType: "text", success: function(response){ console.log(response, i); } }); })(i); }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85771.html
摘要:前端日報精選漫談函數式編程一十年蹤跡的博客前端每周清單的優勢與劣勢有望超越在嵌入式及物聯網的應用現狀進階系列高階組件詳解一前端之路譯如何充分利用控制臺掘金程序猿升級攻略眾成翻譯中文譯如何充分利用控制臺掘金前端從強制開啟壓縮探 2017-06-27 前端日報 精選 漫談 JS 函數式編程(一) - 十年蹤跡的博客前端每周清單: Vue的優勢與劣勢;Node.js有望超越Java;JS在嵌...
摘要:使用緩存兩個前提條件數據訪問熱點不均衡數據某時段內有效,不會很快過期反向代理本地緩存分布式緩存異步旨在系統解耦。 大型網站技術架構-入門梳理 標簽 : 架構設計 [TOC] 羅列了大型網站架構涉及到的概念,附上了簡單說明 前言 本文是對《大型網站架構設計》(李智慧 著)一書的梳理,類似文字版的思維導圖 全文主要圍繞性能,可用性,伸縮性,擴展性,安全這五個要素 性能,可用性,伸縮性...
閱讀 6912·2021-09-22 15:08
閱讀 1920·2021-08-24 10:03
閱讀 2437·2021-08-20 09:36
閱讀 1315·2020-12-03 17:22
閱讀 2474·2019-08-30 15:55
閱讀 905·2019-08-29 16:13
閱讀 3053·2019-08-29 12:41
閱讀 3249·2019-08-26 12:12