js異步執(zhí)行原理&淺談異步與回調(diào)

先說一件大家都知道的一句話

javaScript語言的一大特點是單線程,單線程就是一次他只能做一件事~

其實我也在學(xué)js的時候,在很多博客都看過這句話,好像是一眼就看懂了,但是一直不知道這句話到底是干嘛的,到底有什么用~,所以就注定有些bug還是要踩一次.

第一個因為js是單線程的困惑來自于WebApi的動態(tài)創(chuàng)建元素, 對,異步并不是我在ajax里面學(xué)習(xí)到的,而是DOM里面學(xué)習(xí)到的,但是ajax讓我更深的去理解了什么是異步

先看下第一段讓我困惑,但是其實非常簡單的異步代碼,把核心邏輯簡化后的代碼(其實很簡單,每個人都看得懂)

<div>
  <ul>
  <li>我是第一個li</li>
  <li>我是第二個li</li>
  </ul>
  <input id="btn" type="button" value="動態(tài)生成一個li">
 </div>
  var li = document.getElementsByTagName('li');
 var btn = document.getElementById(('btn'));
 ?
 //動態(tài)創(chuàng)建一個li元素
 btn.onclick = function () {
  var li = document.createElement('li');
  li.innerHTML="我是動態(tài)創(chuàng)建的li";
  document.getElementsByTagName('ul')[0].appendChild(li);
 };
 //給每一個li元素注冊點擊事件,當(dāng)點擊的時候,讓背景顏色變紅
 for (let i = 0; i < li.length; i++) {
  li[i].onclick = function () {
  li[i].style.backgroundColor ='red';
  }
 }
 ?
 console.log(1);
 setTimeout(function () {
  console.log(2)
 }, 0);
 console.log(3);
 //輸出結(jié)果1 3 2

</pre>

執(zhí)行的結(jié)果卻是,動態(tài)創(chuàng)建的li標(biāo)簽,沒有點擊事件...

image.png
image.png

上面這個問題其實對于一個不了解什么是異步的小白來說,真的是一個很大的問題,拿到這個問題后,我開始了一大串的百度,最后,問題沒有得到實質(zhì)的解決,反而拿到一個新的概念,叫"異步";

什么是異步任務(wù)?

簡單的理解就是,js中所有的代碼塊都可以按照任務(wù)分為兩種任務(wù),一種是同步任務(wù),一種是異步任務(wù),而js遇到這兩種任務(wù)的時候,會按照同步和異步兩種類別進行區(qū)別對待.

同步任務(wù)進入主線程,從上往下執(zhí)行,一條一條代碼執(zhí)行,形成一個叫執(zhí)行棧的東西

異步任務(wù)會進入另外一個任務(wù)隊列中,要等待主線程執(zhí)行完了,才會執(zhí)行,

異步任務(wù)包括像需要用戶觸發(fā)的點擊事件,滾動事件,鍵盤事件,定時器等等,我喜歡簡單的去理解 ,我的理解是未來才會發(fā)生的事件就是異步事件

聽起來肯定還是有些復(fù)雜,很抽象,我們用一張圖來表示

image.png
  • 看到上面這張圖中,左邊的同步任務(wù)非常好理解,那就來說一下右邊的異步任務(wù);

  • 當(dāng)事件被識別為異步任務(wù)的時候,瀏覽器會把這個任務(wù)放在 Event Table

  • 這個Event Table會把傳入的異步事件注冊為一個回調(diào)函數(shù),然后傳給Event Queue,

  • 事件注冊為回調(diào)函數(shù)后,并放在Event Queue中等待,那什么時候這個回調(diào)函數(shù)會被執(zhí)行呢?

  • js中引擎中有一個monitoring process進程,在主線程執(zhí)行完畢后 , 會不斷的檢查Event Queue有沒有回調(diào)函數(shù)在等待執(zhí)行,如果有,就把這個回調(diào)函數(shù)放在主線程上執(zhí)行

  • 而這個異步任務(wù)不斷排隊,主線程不斷檢查異步排隊,主線程不斷執(zhí)行的循環(huán)就叫事件循環(huán) Event Loop

了解了事件循環(huán),再來看上面的代碼,就能夠發(fā)現(xiàn)問題的原因:

1.定時器的時間就算是0.他也是一個異步任務(wù),HTML5標(biāo)準(zhǔn)規(guī)定了setTimeout()的第二個參數(shù)的最小值(最短間隔)栅屏,不得低于4毫秒,如果低于這個值,就會自動增加

2.動態(tài)創(chuàng)建元素的問題

 //動態(tài)創(chuàng)建一個li元素
 btn.onclick = function () {
  var li = document.createElement('li');
  li.innerHTML="我是動態(tài)創(chuàng)建的li";
  document.getElementsByTagName('ul')[0].appendChild(li);
 };
 //給每一個li元素注冊點擊事件,當(dāng)點擊的時候,讓背景顏色變紅
 for (let i = 0; i < li.length; i++) {
  li[i].onclick = function () {
  li[i].style.backgroundColor ='red';
  }
 }

1.btn.onclick = function(){} 已經(jīng)被注冊成一個回調(diào)函數(shù),等待在事件隊列中,注冊的回調(diào)函數(shù)中l(wèi)i沒有點擊事件,,此時for循環(huán)已經(jīng)結(jié)束

知道原理后其實簡單的修改一下就可以了,原理就是雖然你在隊列中排隊,但是點擊事件已經(jīng)注冊在排隊里的回調(diào)函數(shù)里面的, 把事件注冊在了未來

 function fn() {
  var li = document.createElement('li');
  li.innerHTML = "我是動態(tài)創(chuàng)建的li";
  document.getElementsByTagName('ul')[0].appendChild(li);
  li.onclick = function () {
  li.style.backgroundColor = 'red';
  }
 }
 btn.onclick = fn;

知道異步后有什么用?

了解異步之后,對于回調(diào)函數(shù)的學(xué)習(xí)能起到一個順?biāo)浦鄣淖饔?而回調(diào)函數(shù)是js語言中非常重要的一個東西, 可以說是js中的精髓了, 異步是未來才會執(zhí)行的事件,而回調(diào)函數(shù)專門用來接收未來才會有結(jié)果的數(shù)據(jù).一張圖來解釋~


image.png

我們知道在ajax中請求拿到的結(jié)果是一個異步操作,而這個結(jié)果,是我在未來才能拿到的一個結(jié)果,而且作為函數(shù)的封裝者,這個未來的結(jié)果,我不能自己用, 而是要把未來的結(jié)果給別人用 . 這就讓人很蒙圈 .話不多數(shù),我們先來看下jQuery封裝的ajax

$.ajax({
  type:'get',
  url:"",
  data:{},
  success:function (data) {
  //我的data是未來(請求成功后)才會有結(jié)果是數(shù)據(jù)
  console.log("成功的回調(diào)函數(shù)")
  }
  })

success是一個異步結(jié)果,我們把他按照異步的原理簡化一下

 //假設(shè)fn是一個幫我在服務(wù)器端拿數(shù)據(jù)的一個函數(shù),拿到后他不能自己處理,因為他的作用是幫我拿數(shù)據(jù)
 function fn(a,callback){
  //我是一個成功后的回調(diào)異步結(jié)果
  setTimeout(function () {
  //我是回調(diào)函數(shù),我把未來才會產(chǎn)生的結(jié)果作為參數(shù)傳給了我的調(diào)用者
  a = 500;
  callback(a);
  },100)
 }
 //我需要用一個數(shù)據(jù),但是這個數(shù)據(jù)我在未來才能用的到,我傳入一個回調(diào)函數(shù),他幫我?guī)砹宋磥淼慕Y(jié)果
 fn(100,function (data){
  var b = 1000;
  console.log(data + b); //1500
 })
  • 第一次js執(zhí)行,fn調(diào)用,傳參100,并在第二個參數(shù)里聲明了一個匿名函數(shù),此時主線程執(zhí)行完畢

  • 事件隊列100毫秒后,把a=500;作為參數(shù)傳入回調(diào)函數(shù),同時調(diào)用回調(diào)函數(shù)

  • fn中的第二個參數(shù)拿到實參并執(zhí)行回調(diào)代碼

  • 這樣callback就完成了他的使命,拿到異步結(jié)果,把結(jié)果作為參數(shù)給調(diào)用者進行運算

最后有興趣的再看下源生的封裝

 function ajax (method, url, params, done) {
  method = method.toUpperCase();
  var xhr = new XMLHttpRequest();
  if (typeof params === 'object') {
  var tempArr = []
  for (var key in params) {
  var value = params[key]
  tempArr.push(key + '=' + value)
  };
  params = tempArr.join('&');
  };
 ?
  if (method === 'GET') {
  url += '?' + params
  };
 ?
  xhr.open(method, url, false);
  var data = null;
  if (method === 'POST') {
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
  data = params
  };
  xhr.onreadystatechange = function () {
  if (this.readyState !== 4) return
  //this,responseText是未來的結(jié)果
  //被done回調(diào)函數(shù)帶走了
  done(this.responseText);
  };
  xhr.send(data);
  };
 // 調(diào)用者============================
 ajax('get', 'time.php', {}, onDone(a){})</pre>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岛心,一起剝皮案震驚了整個濱河市身冀,隨后出現(xiàn)的幾起案子警没,更是在濱河造成了極大的恐慌,老刑警劉巖寺擂,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泼掠,居然都是意外死亡怔软,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門择镇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挡逼,“玉大人,你說我怎么就攤上這事腻豌≈课粒” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵饲梭,是天一觀的道長乘盖。 經(jīng)常有香客問我,道長憔涉,這世上最難降的妖魔是什么订框? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮兜叨,結(jié)果婚禮上穿扳,老公的妹妹穿的比我還像新娘。我一直安慰自己国旷,他們只是感情好矛物,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著跪但,像睡著了一般履羞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天忆首,我揣著相機與錄音爱榔,去河邊找鬼。 笑死糙及,一個胖子當(dāng)著我的面吹牛详幽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播浸锨,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼唇聘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了柱搜?” 一聲冷哼從身側(cè)響起雳灾,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冯凹,沒想到半個月后谎亩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡宇姚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年匈庭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浑劳。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡阱持,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出魔熏,到底是詐尸還是另有隱情衷咽,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布蒜绽,位于F島的核電站镶骗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躲雅。R本人自食惡果不足惜鼎姊,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望相赁。 院中可真熱鬧相寇,春花似錦、人聲如沸钮科。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绵脯。三九已至休里,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間植兰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工璃吧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留楣导,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓畜挨,卻偏偏與公主長得像筒繁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子巴元,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容