摘要:而異步則是相反,調用在發出之后,這個調用就直接返回了,所以沒有返回結果而是在調用發出后,被調用者通過狀態通知來通知調用者,或通過回調函數處理這個調用。總結回調函數是異步編程中的基石,但同時也存在很多問題,不太適合人類自然語言的線性思維習慣。
為什么 JS 是單線程?
眾所周知,Javascript 語言的執行環境是"單線程"(single thread)。
所謂"單線程",就是指一次只能完成一件任務。如果有多個任務,就必須排隊,前面一個任務完成,再執行后面一個任務,以此類推。
而瀏覽器是多線程的,JS 線程就是其中一個:
瀏覽器 GUI 渲染線程
JavaScript 引擎線程
瀏覽器定時觸發器線程
瀏覽器事件觸發線程
瀏覽器 http 異步請求線程
瀏覽器線程知識中重要的一點是:
GUI渲染進程和 JavaScript 引擎進程是互斥的,因為如果這兩個線程可以同時運行的話, JavaScript 的 DOM 操作將會擾亂渲染線程執行渲染前后的數據一致性。而且如果 DOM 一變化,界面就立刻重新渲染,效率必然很低
所以 JS 主線程執行任務時,瀏覽器渲染線程處于掛起狀態。
同理,如果 JS 采用多線程同步的模型,那么如何保證同一時間修改了 DOM, 到底是哪個線程先生效呢?從操作系統調度多線程的上下文開銷,到實際編程里的鎖、線程同步等問題,都讓開發變得比較困難。
所以 JS 最終采用了單線程的事件模型。
我之前的文章《JS專題之事件循環》也有講過這塊內容,歡迎翻閱。
一、同步與異步單線程模式這種排隊執行的好處是實現起來比較簡單,執行環境相對單純;壞處是只要有一個任務耗時很長,后面的任務都必須排隊等著,會拖延整個程序的執行。常見的瀏覽器無響應(假死),往往就是因為某一段Javascript代碼長時間運行(比如死循環),導致整個頁面卡在這個地方,其他任務無法執行。
為了解決這個問題,Javascript語言將任務的執行模式分成兩種:同步(Synchronous)和異步(Asynchronous)。
那同步和異步的區別是什么?
我們想象一個很常見的場景:我們去面館吃牛肉面,柜臺人很多,前面在排隊下單。
這個時候,同步就是,收銀員收了你的錢,告訴你要在柜臺站著等面煮好,煮好后,就端面開吃,后面的人也只能等前面的人面煮好了才能付款下單然后等著面煮好端走~
而異步就是,收銀員收了你的錢,然后給了你一張小票,小票上有一個你的編號,收銀員告訴你,可以去座位上,你的面一煮好,會大聲叫你,你就來端面開吃。
我們可以看出,我們是過程的調用者,面館是被調用者,牛肉面煮好,是我們想要的結果,同步是調用者需要主動地等待這個結果。異步是被動的等待結果,當被調用者有結果了,就會通過消息機制或者回調機制告訴調用者結果。
同步和異步關注的是消息通信機制,同步就是在發出一個調用時,在沒有得到結果之前,該調用就不返回。但是一旦調用返回,就得到返回值了。而異步則是相反,調用在發出之后,這個調用就直接返回了,所以沒有返回結果, 而是在調用發出后,被調用者通過狀態、通知來通知調用者,或通過回調函數處理這個調用。
以上:
下單吃面是發起調用函數
端面開吃的回調函數
煮好的面是調用的結果,也是回調函數的參數
將例子抽象成偽代碼:
orderNoodle("牛肉面", function(noodle) { // 端面 getNoodle(); // 吃面 eatNoodle(); });三、事件循環
關于事件循環如何執行異步代碼可以翻閱前面的文章《JS專題之事件循環》,這里大概提一下。
如果遇到異步事件,JS 引擎會把事件函數壓入執行調用棧,但瀏覽器識別到它是異步事件后,會將其彈出執行棧,當異步函數有返回結果后,JS 引擎將異步事件的回調函數放入事件隊列中,如果執行調用棧為空,就將回調函數壓入執行調用棧執行。
四、回調函數在 JavaScript 中,函數 function 作為一等公民,使用上非常自由,無論調用它,或者作為參數,或者作為返回值都可以。
因為單線程異步的特點,后來在 JS 中,慢慢將函數的業務重點轉移到了回調函數中。
function step1(cb) { console.log("step1"); cb() } function step2(){ console.log("step2"); } step1(step2); // step1 step2
代碼會按先后順序執行 step1, step2。
現在假設我們有這樣的需求:請求文件1后,獲取文件1 中的數據后請求文件2,獲取文件 2 中的數據后,又請求文件三。
var fs = require("fs"); fs.readFile("./file1.json", function(err, data1) { fs.readFile("./file2.json", function (err, data2) { fs.readFile("./file3.json", function(err, data3) { }) }) })五、回調函數的問題
由第四節可以看出,回調函數的寫法存在很多問題。
回調地獄(洋蔥模型)
當多個異步事務多級依賴時,回調函數會形成多級的嵌套,被花括號一層層包括,代碼變成
金字塔型結構,也被稱為回調地獄和洋蔥模型。
在回調地獄的情況下,代碼邏輯的梳理,流程的控制,代碼封裝維護,錯誤處理都變得越來越困難。
異常處理
try...catch 是被設計成捕獲當前執行環境的異常,意思是只能捕獲同步代碼里面的異常,異步調用里面的異常無法捕獲。
function readFile(fileName) { setTimeout(function () { throw new Error("類型錯誤"); }, 1000); } try { readFile("./file1.json"); } catch (e) { // 如果異步事件出錯,打印不出來錯誤信息 console.log("err", e); }
在 nodejs 對回調函數采用 error first 的思想,回調函數的第一個參數保留給一個錯誤error對象,如果有錯誤發生,錯誤將通過第一個參數err返回。
原因是一個有回調函數的函數,執行分兩段,第一段執行完之后,任務所在的上下文環境就已經結束了。在這以后拋出的錯誤,原來的上下文已經無法捕捉,只能當做參數,傳入第二階段。
fs.readFile("/etc/passwd", "utf8", function (err, data) { if(err) { console.log(err) return; } });總結
回調函數是 JS 異步編程中的基石,但同時也存在很多問題,不太適合人類自然語言的線性思維習慣。
接下來幾篇文章,我將梳理 JS 中異步編程中的歷史演進中 Promise, generator, async&await 相關的內容,歡迎關注。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101762.html
摘要:因為瀏覽器環境里是單線程的,所以異步編程在前端領域尤為重要。除此之外,它還有兩個特性,使它可以作為異步編程的完整解決方案函數體內外的數據交換和錯誤處理機制。 showImg(https://segmentfault.com/img/bVz9Cy); 在我們日常編碼中,需要異步的場景很多,比如讀取文件內容、獲取遠程數據、發送數據到服務端等。因為瀏覽器環境里Javascript是單線程的,...
摘要:傳統的異步方法回調函數事件監聽發布訂閱之前寫過一篇關于的文章,里邊寫過關于異步的一些概念。內部函數就是的回調函數,函數首先把函數的指針指向函數的下一步方法,如果沒有,就把函數傳給函數屬性,否則直接退出。 Generator函數與異步編程 因為js是單線程語言,所以需要異步編程的存在,要不效率太低會卡死。 傳統的異步方法 回調函數 事件監聽 發布/訂閱 Promise 之前寫過一篇關...
javascript -- 回調函數 在高級語言層出不窮的年代, 各個語言都號稱有著一切皆為對象的自豪說法, 而 js 作為一門腳本語言卻相對于java等傳統面向對象語言有很大的不同之處, 除了 js 詭異的繼承體系之外, 最令人著迷的一個特性就是回調函數, 當然也有很多人對他詬病, 筆者認為 回調函數 和 異步 是js語言特性的兩大最為突出的店, 當然正如所有優點需要滿足自我的需求, 這個世界...
摘要:異步編程是編寫的一個很重要的理念,特別是在處理復雜應用的時候,異步編程的技巧就至關重要。那么下面就來看看這個被稱為里程碑式的異步編程庫吧。 1. 前言 最近在看司徒正美的《JavaScript框架設計》,看到異步編程的那一章介紹了jsdeferred這個庫,覺得很有意思,花了幾天的時間研究了一下代碼,在此做一下分享。 異步編程是編寫js的一個很重要的理念,特別是在處理復雜應用的時候,異...
摘要:不少第三方模塊并沒有做到異步調用,卻裝作支持回調,堆棧的風險就更大。我們可以編寫一個高階函數,讓傳入的函數順序執行還是我們之前的例子看起來還是很不錯的,簡潔并且清晰,最終的代碼量也沒有增加。 原文: http://pij.robinqu.me/JavaScript_Core/Functional_JavaScript/Async_Programing_In_JavaScript....
閱讀 3581·2023-04-26 02:55
閱讀 2849·2021-11-02 14:38
閱讀 4135·2021-10-21 09:39
閱讀 2842·2021-09-27 13:36
閱讀 3943·2021-09-22 15:08
閱讀 2643·2021-09-08 10:42
閱讀 2802·2019-08-29 12:21
閱讀 667·2019-08-29 11:22