JavaScript異步基礎

唯一比不知道代碼為什么崩潰更可怕的事情是,不知道為什么一開始它是工作的!

在 ECMA 規范的最近幾次版本里不斷有新成員加入,尤其在處理異步的問題上,更是不斷推陳出新。然而,我們在享受便利的同時,也應該了解異步到底是怎么一回事。

現在與將來

JavaScript 是單線程的,一次只能專注于一件事。如果瀏覽器只靠 JavaScript 引擎線程來完成所有工作,先不說能不能搞定,即使可以,那也會花費很長時間。幸好在瀏覽器里 JavaScript 引擎并不孤單,還有 GUI 渲染線程、事件觸發線程、定時觸發器線程、異步http請求線程等其它線程。這些線程之間的協作才有了我們看到的瀏覽器界面效果(遠不止這些)。

(盜了一張圖)
thread

一個程序在執行過程中可能會有等待用戶輸入、從數據庫或文件系統中請求數據、通過網絡發送并等待響應,或是以固定時間間隔執行重復任務(比如動畫)等情況。(這些情況,當下是無法得出結果的,但是一旦有了結果,我們知道需要去做些什么。)

JavaScript 引擎不是一個人在戰斗,它把以上的任務交給其它線程,并計劃好任務完成后要做的事,JavaScript 引擎又可以繼續做自己的事了。從這里可以看出,一個程序的運行包括兩部分,現在運行和將來運行。而現在運行和將來運行的關系正是異步編程的核心。

let params = {type:'asynchronous'}
let response = ajax(params,'http://someURL.com'); // 異步請求
if (!response) throw '無數據!';

以上代碼肯定會拋錯的,異步請求任務交出去之后,程序會繼續運行下去。由于ajax(...) 是異步操作,即使立刻返回結果,當下的 response 也不會被賦值。一個是現在,一個是將來,兩者本就不屬于一個時空的。

事件循環

現在和將來是相對的,等將來的時刻到了,將來也就成為了現在。

JavaScript 引擎運行在宿主環境中,宿主環境提供了一種機制來處理程序中多個塊的執行,且執行每個塊時調用 JavaScript 引擎,這種機制被稱為事件循環。即,JavaScript 引擎本身并沒有時間的概念,只是一個按需執行 JavaScript 任意代碼片段的環境。

“事件”(JavaScript 代碼執行)調度總是由包含它的環境進行。

點擊圖片進入或點此進入:
EventLoop

一個 JavaScript 運行時包含了一個待處理的消息隊列。每一個消息都關聯著一個用以處理這個消息的函數。
在事件循環期間的某個時刻,運行時從最先進入隊列的消息開始處理隊列中的消息。為此,這個消息會被移出隊列,并作為輸入參數調用與之關聯的函數。

while (queue.waitForMessage()) {queue.processNextMessage();
}

一旦有事件需要進行,事件循環就會運行,直到隊列清空。事件循環的每一輪稱為一個 tick。用戶交互,IO 和定時器會向事件隊列中加入事件。

(又盜了一張圖)
thread

任務隊列

任務隊列(job queue)建立在事件循環隊列之上。(Promise 的異步特性就是基于任務。)

最好的理解方式,它是掛在事件循環隊列的每個tick之后的一個隊列。在事件循環的每個tick中,可能出現的異步動作不會導致一個完整的新事件添加到事件循環隊列中,而會在當前 tick 的任務隊列末尾添加一個項目(一個任務)。
即,由 Call Stack 生成的任務隊列會緊隨其后運行。

Promise.resolve().then(function promise1 () {console.log('promise1');
})
setTimeout(function setTimeout1 (){console.log('setTimeout1');Promise.resolve().then(function  promise2 () {console.log('promise2');})
}, 0)setTimeout(function setTimeout2 (){
console.log('setTimeout2');Promise.resolve().then(function  promise3 () {console.log('promise3');setTimeout(function setTimeout3 () {console.log('setTimeout3');})Promise.resolve().then(function promise4 () {console.log('promise4');})})
}, 0)
// promise1
// setTimeout1
// promise2
// setTimeout2
// promise3
// promise4
// setTimeout3

異步回調

被作為實參傳入另一函數,并在該外部函數內被調用,用以來完成某些任務的函數,稱為回調函數。回調函數經常被用于繼續執行一個異步完成后的操作,它們被稱為異步回調。立即執行的稱之為同步回調。

回調函數是事件循環“回頭調用”到程序中的目標,隊列處理到這個項目的時候會運行它。

回調是 JavaScript 語言中最基礎的異步模式。

生活中,我們喜歡和有條理的人打交道,因為我們的大腦習慣了這種思維模式。然而回調的使用打破了這種模式,因為代碼的嵌套使得我們要在不同塊間切換。嵌套越多,邏輯越復雜,我們也就越難理解和處理代碼,尤其在表達異步的方式上。

(又盜了一張圖)
回調地獄

除了嵌套的問題,異步回調還存在一些信任問題。

  • 回調性質的不確定
  • 調用回調方式不確定(沒調用,重復調用等)
  • ......

針對第一點的建議是:永遠異步調用回調,即使就在事件循環的下一輪,這樣,所有回調都是可預測的異步調用了。
在理解這個建議之前,我們首先了解下控制反轉,控制反轉就是把自己程序一部分的執行控制交個某個第三方。

let a = 0; // A
thirdparty(() => {console.log('a', a);    // B
})
a++;    // C

A 和 C 是現在運行的,B 雖然代碼是我們的,但是卻受制于第三方,因為我們無法確定它是現在運行還是將來運行的。這里的回調函數可能是同步回調也可能是異步回調。a 是 0 還是 1,都有可能。

// 同步回調
const thirdparty = cb => {cb();
}
// 異步回調
const thirdparty = cb => {setTimeout(() => cb(), 0);
}

所以,永遠異步調用回調,可預測。

function asyncify(fn) {let func = fn;let t = setTimeout(() => {t = null;if (fn) fn();}, 0);fn = null;return () => {if (t) {fn = func.bind(this, ...arguments);} else {func.apply(this, arguments);}}
}let a = 0;
thirdparty(asyncify(() => {console.log('a', a);
}))
a++;
// 1

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/536949.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536949.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536949.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Flutter、ReactNative、uniapp對比

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

JavaScript數組方法

一、基本類型和引用類型 數值、字符串、布爾值、undefined、null可以直接寫出來,比較簡單的數據稱為基本類型,在比較的時候,是直接按值比較。對象、函數、數組復雜的數據是引用類型,在比較的時候,是按照地址比較。cons…

nodejs mysql模塊_NodeJs使用Mysql模塊實現事務處理

依賴模塊:1. mysql:https://github.com/felixge/node-mysqlnpm install mysql --save2. async:https://github.com/caolan/asyncnpm install async --save(ps: async模塊可換成其它Promise模塊如bluebird、q等)因為Node.js的mysql模塊本身對于…

計數排序vs基數排序vs桶排序

從計數排序說起 計數排序是一種非基于元素比較的排序算法,而是將待排序數組元素轉化為計數數組的索引值,從而間接使待排序數組具有順序性。 計數排序的實現一般有兩種形式:基于輔助數組和基于桶排序。 基于輔助數組 整個過程包含三個數組&…

多線程中ThreadLocal的使用

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

mysql 查看所有表的引擎_MySQL查看數據庫、表的占用空間大小以及某個庫中所有表的引擎類型...

本文章來給大家介紹一些常用的MySQL查看數據庫、表的占用空間大小sql命令吧,希望此教程 對各位同學會有所幫助。查看各庫的大小代碼如下復制代碼SELECT SUM(DATA_LENGTH)SUM(INDEX_LENGTH) FROM information_schema.tables WHERE TABLE_SCHEMAdatabase_name;結果是以…

Fusion組件庫是如何支持多語言能力的

隨著國際化發展,多語言的需求越來越常見,單一的語言已經遠不能滿足需求了。作為一個組件庫,支持多語言也是基本能力。 多語言功能的本質其實是文本的替換,一個詞匯“OK”,在英文語境下是“OK”,日語語境下是…

mysql 存儲過程 replace_mysql replace存儲過程

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云數據庫專家保駕護航,為用戶…

注解版poi操作工具

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Kali Linux 2019.1 發布,Metasploit 更新到 5.0 版本

百度智能云 云生態狂歡季 熱門云產品1折起>>> Kali Linux 2019.1 發布了,Kali 前身 BackTrack,它是一個基于 Debian 的 Linux 發行版,主要用于信息安全行業,其包含了一系列安全、滲透測試和取證工具。此版本 Linux 內核…

peewee mysql_scrapy中利用peewee插入Mysql

前兩天老大布置一個任務,說爬下來的數據要存入數據庫中,丟給我一個peewee,說用這個。當時的我兩眼一抹黑,這是個什么東西呀,我知道scrapy的數據存入數據庫是在pipelines中進行設置但是peewee是什么東西呢。經過兩天不懈…

Java版數據結構與算法——線性表

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

基于 CODING 的 Spring Boot 持續集成項目

本文作者:CODING 用戶 - 廖石榮 持續集成的概念 持續集成(Continuous integration,簡稱 CI)是一種軟件開發實踐,即團隊開發成員經常集成他們的工作,通常每個成員每天至少集成一次,也就意味著每天可能會發生多次集成。每…

lvs mysql 端口_LVS配置及多端口服務配置

一、5、各主機IP地址:主機IP網關Client192.168.86.116RouterF0/0:192.168.x.xFo/1:192.168.xx.xxF0/1DirectorEth0:192.168.86.111/24(DIP)Eth0:1:192.168.86.254/32(VIP)F0/1Real 1Eth0:192.168.86.112/24(DIP)lo:1:192.168.86.254/32(VIP)F0/1Real 2Eth0:192.168.…

Mybatis組成部分

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Stream流與Lambda表達式(一) 雜談

一、流 轉換為數組、集合 package com.java.design.java8.Stream;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner;import java.util.A…

一年java工作經驗-面試總結

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

linux mysql python包_03_mysql-python模塊, linux環境下python2,python3的

---恢復內容開始---1、Python2 正常[rootIP ~]#pip install mysql-pythonDEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 wont be maintained after that date. A future version of pip will drop …

我的這套VuePress主題你熟悉吧

最近熬了很多個夜晚, 踩坑無數, 終于寫出了用VuePress驅動的主題. 只需體驗三分鐘,你就會跟我一樣,愛上這款主題. vuepress-theme-indigo-material, 已經發布到npm, 請客官享用~~ 介紹 vuepress-theme-indigo-material 的原主題是hexo-theme-indigo, git…

兩年Java工作經驗應該會些什么技術

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…