摘要:異步與同步技術(shù)研究概念介紹異步簡寫同步簡寫用比方來比喻異步就是個人同時起跑起點和出發(fā)時間相同在起跑時不去關(guān)心其他人會啥時候跑完尼瑪這不廢話嗎大家都才起跑怎么知道別人多就跑完同步就是個人接力跑起點和出發(fā)時間不同且后一個人會等待前一個人跑完才能
1. 異步與同步 技術(shù)研究 (1). 概念介紹
異步: asynchronous 簡寫async
同步: synchronous 簡寫sync
用比方來比喻
異步就是: N個人同時起跑, 起點和出發(fā)時間相同, 在起跑時不去關(guān)心其他人會啥時候跑完~尼瑪這不廢話嗎?大家都才起跑怎么知道別人多就跑完.
同步就是: N個人接力跑, 起點和出發(fā)時間不同, 且后一個人會等待前一個人跑完才能繼續(xù)跑, 也就是要關(guān)心前一個人的結(jié)果(上一行代碼的返回值).
JS運行場景多數(shù)是在用戶瀏覽器上, 程序效率優(yōu)劣會直接影響用戶的體驗交互. 比如一個網(wǎng)站, 在用戶注冊時, 會ajax校驗輸入再發(fā)提交表單, 如果用同步就可能會一直卡著等待ajax響應(yīng), 好幾秒結(jié)束后再跳到注冊結(jié)果頁, 這個體驗將是非常糟糕的.
說到JS的異步, 不得不提及一個非常有代表意義函數(shù)了.
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中均稱為回調(diào)方法,
以原生JS的XMLHttpRequest為例, xmlHTTP變量是個XMLHttpRequest對象, 他的onreadystatechange是在每次請求響應(yīng)狀態(tài)發(fā)生變化時會觸發(fā)的一個函數(shù)/方法, 然后在發(fā)出請求xmlHTTP.send(data)的時候, JS并不會理會onreadystatechange方法, 而當(dāng)改送請求到達服務(wù)器, 開始響應(yīng)或者響應(yīng)狀態(tài)改變時會調(diào)用onreadystatechange方法:
也就是
1) 請求發(fā)出
2) 服務(wù)器開始響應(yīng)數(shù)據(jù)
3) 執(zhí)行回調(diào)方法, 可能執(zhí)行多次
以jQuery版為例, $.ajax本身是個函數(shù), 唯一一個參數(shù)是{...} 這個對象, 然后回調(diào)方法success是作為這個對象的一個屬性傳入$.ajax的.
$.ajax()先將數(shù)據(jù)post到"/action/", 返回結(jié)果后再調(diào)用success(如果發(fā)生錯誤會調(diào)用error).
也就是
1) 請求發(fā)出 2) 服務(wù)器開始響應(yīng)數(shù)據(jù) 3) 響應(yīng)結(jié)束執(zhí)行回調(diào)方法
然后作為函數(shù)$.ajax, 是函數(shù)就應(yīng)該有返回值(哪怕沒有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未將結(jié)果返回... 而async:false就將結(jié)果返回了.
然后問題就來了, 為什么async:true未返回結(jié)果呢?
答案很簡單:
因為在返回的時候, 程序不可能知道結(jié)果. 異步就是指不用等此操作執(zhí)行出結(jié)果再往下執(zhí)行, 也就是返回的值中未包含結(jié)果.留下一個問題, 我們是不是為了程序流程的簡單化而使用同步呢?
(3). 異步的困惑先帖一段代碼:
a.phpphppage.js
JavaScriptfor( i = 1; i <= 4; i++ ){ $.ajax({ url: "a.php", type: "POST", dataType: "json", data: {data: i}, async: true, // 默認(rèn)即為異步 success: function(json) { console.log(i + ": " + json); // 打印 } }); }你們猜猜打印的那行會最終打印出什么內(nèi)容?
是1: {} 2: {} 3: {} 4: {}嗎?
錯!
輸出的將是:
4: {} 4: {} 4: {} 4: {}你TM在逗我?
沒有, 這并不是JS的BUG, 也不是jQuery的BUG.
這是因為, PHP休息了一秒, 而js異步地循環(huán)從1到4, 遠遠用不到1秒.
然后在1秒鐘后, 才開始返回數(shù)據(jù), 觸發(fā)success, 此時此刻i已經(jīng)自增成了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) 給回調(diào)的事件對象賦屬性
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) { // 增加回調(diào)參數(shù), jQuery文檔有說第三個參數(shù)就是ajax方法產(chǎn)生的對象. console.log(obj.i + ": " + json); // 從jQuery.ajax返回的對象中取i } }); ajaxObj.i = i; // 給ajaxObj賦屬性i 值為循環(huán)的i }有可能你會感到困惑, 為何可以給ajaxObj設(shè)置一個i屬性然后在回調(diào)時用第三個回調(diào)參數(shù)的i屬性呢?jQuery.ajax文檔中寫到:
jQuery.ajax( [settings ] ) settings ... success: Function( Anything data, String textStatus, jqXHR jqXHR ) 第1個參數(shù)就是響應(yīng)的文本/HTML/XML/數(shù)據(jù)/json之類的, 跟你的dataType設(shè)置有關(guān) 第2個參數(shù)就是status狀態(tài), 如success 第3個參數(shù)就是jqXHR, 也就是jQuery的XMLHttpRequest對象, 當(dāng)然, 在這里就是$.ajax()生成的對象, 也就是事件的觸發(fā)者本身, 給本身設(shè)置一個屬性(ajaxObj.i = i), 然后再調(diào)用本身的回調(diào)時, 使用本身的那個屬性(obj.i), 當(dāng)然會保持一致了.然后
1)輸出的結(jié)果將是1: {i:1} 2: {i:2} 3: {i:3} 4: {i:4}2)輸出的結(jié)果將是
1: {} 2: {} 3: {} 4: {}雖然略有區(qū)別, 但兩者均可達到要求. 若要論代碼的逼格, 相信你一定會被第二個方案給震驚的.
憑什么你給ajaxObj賦個屬性就可以在success中用了呢?請看(4). 異步的回調(diào)機制
(4). 異步的回調(diào)機制 ------ 事件一個有經(jīng)驗的JavaScript程序員一定會將js回調(diào)用得得心應(yīng)手.
因為JavaScript天生異步, 異步的好處是顧及了用戶的體驗, 但壞處就是導(dǎo)致流程化循環(huán)或者遞歸的邏輯明明在別的語言中無任何問題, 卻在js中無法取得期待的值...
而JavaScript異步在設(shè)計之初就將這一點考慮到了. 任何流行起來的JS插件方法, 如jQuery的插件, 一定考慮到了這一點了的.舉個例子.
ajaxfileupload插件, 實現(xiàn)原理是將選擇的文件$.clone到一個form中, form的target設(shè)置成了一個頁面中的iframe, 然后定時取iframe的contents().body, 即可獲得響應(yīng)的值. 如果要支持multiple文件上傳(一些現(xiàn)代化的瀏覽器支持), 還是得要用`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為觸發(fā)這個事件的對象 即xmlHTTP (XMLHttpRequest) 對象 try{ console.log(a.target.responseText); }catch(e){ return false; } }; xmlHTTP.send(data); } })你可以很明顯地知道, 在onreadystatechange調(diào)用且走到console.log(a.target.responseText)時, 如果服務(wù)器不返回文件名, 我們根本并不知道返回的是哪個文件的URL. 如果根據(jù)i去取的話, 那么很容易地, 我們只會取到始終1個或幾個, 并不能保證準(zhǔn)確.
那么我們應(yīng)該怎么去保證在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的態(tài)度在你嘗試在chrome打開的頁面中執(zhí)行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補充:
異步的數(shù)據(jù)一致性問題也可以用v1兄提供的方法來解決:for(i=1; i < 4; i++){ (function(i){ $.ajax({ url: "URL", type: "get", dataType: "text", success: function(response){ console.log(response, i); } }); })(i); }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/85771.html
摘要:前端日報精選漫談函數(shù)式編程一十年蹤跡的博客前端每周清單的優(yōu)勢與劣勢有望超越在嵌入式及物聯(lián)網(wǎng)的應(yīng)用現(xiàn)狀進階系列高階組件詳解一前端之路譯如何充分利用控制臺掘金程序猿升級攻略眾成翻譯中文譯如何充分利用控制臺掘金前端從強制開啟壓縮探 2017-06-27 前端日報 精選 漫談 JS 函數(shù)式編程(一) - 十年蹤跡的博客前端每周清單: Vue的優(yōu)勢與劣勢;Node.js有望超越Java;JS在嵌...
摘要:使用緩存兩個前提條件數(shù)據(jù)訪問熱點不均衡數(shù)據(jù)某時段內(nèi)有效,不會很快過期反向代理本地緩存分布式緩存異步旨在系統(tǒng)解耦。 大型網(wǎng)站技術(shù)架構(gòu)-入門梳理 標(biāo)簽 : 架構(gòu)設(shè)計 [TOC] 羅列了大型網(wǎng)站架構(gòu)涉及到的概念,附上了簡單說明 前言 本文是對《大型網(wǎng)站架構(gòu)設(shè)計》(李智慧 著)一書的梳理,類似文字版的思維導(dǎo)圖 全文主要圍繞性能,可用性,伸縮性,擴展性,安全這五個要素 性能,可用性,伸縮性...
摘要:為此決定自研一個富文本編輯器。本文,主要介紹如何實現(xiàn)富文本編輯器,和解決一些不同瀏覽器和設(shè)備之間的。 對ES6Generator函數(shù)的理解 Generator 函數(shù)是 ES6 提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同。 JavaScript 設(shè)計模式 ② 巧用工廠模式和創(chuàng)建者模式 我為什么把他們兩個放在一起講?我覺得這兩個設(shè)計模式有相似之處,有時候會一個設(shè)計模式不能滿...
閱讀 7016·2021-09-22 15:08
閱讀 2045·2021-08-24 10:03
閱讀 2531·2021-08-20 09:36
閱讀 1470·2020-12-03 17:22
閱讀 2536·2019-08-30 15:55
閱讀 990·2019-08-29 16:13
閱讀 3140·2019-08-29 12:41
閱讀 3331·2019-08-26 12:12