js 線程 之 異步編程(1)

----歡迎查看我的博客----

js 單線程

??相信大家玩 js 很久了,Js語言的執(zhí)行環(huán)境是 “ 單線程 ”。什么是單 線程,就是當(dāng)一個任務(wù)完成之后才會有下一個任務(wù) 。 舉個栗子,你在奶茶店買奶茶 ,只有一個 奶茶妹妹,客人們都在排隊,問你喝什么,你告訴她,她下好單再叫下一個,以此類推,這就是 單線程的同步。再比如到你了,你沒選好喝什么,她先問下一位喝什么,等你想好了,再問你喝什么,這就是 單線程的異步。多個奶茶妹妹同事問客人 ,就是多線程。

js 為什么是單線程的。

??為什么 JavaScript 不能有多個 線程 呢?這樣能提高效率啊。辦事快啊, 我能撩到好幾個奶茶妹妹,但這個和 js 的作用有關(guān) ,當(dāng)初 js 只是為了服務(wù)于 游覽器 ,用戶交互,所以設(shè)計了單 線程 , 如果多 線程 你想有多么麻煩,你操作了 dom 是修改 ,同時另外的線程執(zhí)行了 刪除 dom 這時候游覽器怎么選擇呢?為了利用多核CPU的計算能力,HTML5提出Web Worker標準,允許JavaScript腳本創(chuàng)建多個線程,但是子線程完全受主 線程 控制,且不得操作DOM。所以,這個新標準并沒有改變JavaScript單線程的本質(zhì)。所以我們?nèi)稳?堅持說 js 是單 線程 的。

異步同步

??既然 js 是 單線程 的,那么按照我們的之前的栗子, 你去買奶茶,你沒想好要喝什么,一直占著位置,估計后面的兄弟們對你肯定不爽了,開始罵娘了 ,所以 這時候我們的 奶茶妹 就很機智,然你在后面等等,先問下一個要喝什么。于是,所有任務(wù)可以分成兩種。

同步任務(wù)(synchronous)

在主線程上排隊執(zhí)行的任務(wù),只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù);

執(zhí)行過程大概是這樣:
(1)所有同步任務(wù)都在主線程上執(zhí)行,形成一個執(zhí)行棧(execution context stack)。
(2)一旦"執(zhí)行棧"中的當(dāng)前任務(wù)執(zhí)行完畢,就會執(zhí)行下一個任務(wù),進入執(zhí)行棧,開始執(zhí)行。
(3)主線程不斷重復(fù)上面的(2)

異步任務(wù)(asynchronous)

??不進入主線程、而進入”任務(wù)隊列”(task queue)的任務(wù),只有”任務(wù)隊列”通知主線程,某個異步任務(wù)可以執(zhí)行了,該任務(wù)才會進入主線程執(zhí)行。
執(zhí)行過程大概是這樣:
(1)所有同步任務(wù)都在主線程上執(zhí)行,形成一個執(zhí)行棧(execution context stack)。
(2)主線程之外,還存在一個"任務(wù)隊列"(task queue)。只要異步任務(wù)有了運行結(jié)果,就在"任務(wù)隊列"之中放置一個事件。
(3)一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會讀取"任務(wù)隊列",看看里面有哪些事件。開始執(zhí)行。
(4)主線程不斷重復(fù)上面的(3)

瀏覽器不是單線程的

雖然JS運行在瀏覽器中,是單線程的,每個window一個JS線程,但瀏覽器不是單線程的,可能有如下線程:

javascript引擎線程

這個就是我們常說的單線程

界面渲染線程

渲染dom頁面的進程

瀏覽器事件觸發(fā)線程

有可能觸發(fā)多個事件。例如 keyup ,clickup 等。

Http請求線程

你可以發(fā)起多個請求,至于最多能發(fā)送多少請求,我們后面會提到。

如果js是單線程的,那么誰去輪詢大的Event loop事件隊列?答案是瀏覽器會有單獨的線程去處理這個隊列。

js 的 異步編程

??異步編程有這么幾種方式:

ES 6以前:

  • 回調(diào)函數(shù)
  • 事件監(jiān)聽(事件發(fā)布/訂閱)

ES 6:

  • Generator函數(shù)(協(xié)程coroutine)
  • Promise對象

ES 7:

  • async和await

* 回調(diào)函數(shù)

 //一個定時器
 function timer(time, callback){
     setTimeout(function(){
         callback();
     }, time);
 };
  
 timer(3000, function(){
      console.log(123);
 })

?? 這個最常見,也是我們經(jīng)常寫的。非常簡單。一個 setTimeout 然后執(zhí)行后面的函數(shù)。至于 setTimeout 的話 ,我們后面再講。

* 事件監(jiān)聽(發(fā)布/訂閱)

??發(fā)布—訂閱模式可以廣泛應(yīng)用于異步編程中,這是一種替代傳遞回調(diào)函數(shù)的方案。比如,我們可以訂閱 ajax 請求的 error 、 succ 等事件。 或者如果想在動畫的每一幀完成之后做一些事情,那我們可以訂閱一個事件,然后在動畫的每一幀完成之后發(fā)布這個事件。在異步編程中使用發(fā)布—訂閱模式,我們就無需過多關(guān)注對象在異步運行期間的內(nèi)部狀態(tài),而只需要訂閱感興趣的事件發(fā)生點。
??這里不詳細說了,因為后面我們會把這種設(shè)計模式單獨拿出來講。這里舉例一個事件監(jiān)聽,其實是一樣的:

element.addEventListener("click",function(){
    alert("clicked");
})

* Generator函數(shù)

這個函數(shù)我用的很少,所以我打算后面專門也來研究一下這個東西。
形式上,Generator 函數(shù)是一個普通函數(shù),但是有兩個特征。

function關(guān)鍵字與函數(shù)名之間有一個星號;

函數(shù)體內(nèi)部使用yield語句,定義不同的內(nèi)部狀態(tài)

我們來大概看看這個東西

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();

hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }

Generator函數(shù)的調(diào)用方法與普通函數(shù)一樣,也是在函數(shù)名后面加上一對圓括號。不同的是,調(diào)用Generator函數(shù)后,該函數(shù)并不執(zhí)行,返回的也不是函數(shù)運行結(jié)果,而是一個指向內(nèi)部狀態(tài)的指針對象,我們可以通過調(diào)用 next 方法,使得指針移向下一個狀態(tài)。

* Promise對象

??這個就比較熟了,之前有提到。不太熟悉的童鞋們可以看這篇文章: 你就需要這篇文章,帶你搞懂實戰(zhàn)的 Promise 對象 ; 后面我們也會將他拿出來,看看我們的 JavaScript 原生如何實現(xiàn) promise 對象。

* async和await

??這個也比較熟悉,之前的博客也提到過 ,了解 JavaScript ES7 的 async / await , ; 后面我們也會將他拿出來,看看我們的 JavaScript 原生如何實現(xiàn) async / await 對象。

結(jié)語

??以上就是我們的異步編程方式,我們后面會慢慢將這些東西拿出來講解 :

  • setTimeout
  • 發(fā)布訂閱
  • 原生(es2015)實現(xiàn) promiss
  • Generator
  • 原生(es2015)實現(xiàn) async / await
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容