Javascript异步编程之一异步原理
生活随笔
收集整理的這篇文章主要介紹了
Javascript异步编程之一异步原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本系列的例子主要針對node.js環境,但瀏覽器端的原理應該也是類似的。
本人也是Javascript新手,把自己這段時間學習積累的要點總結下來,希望可以對同樣在學習Javascript/node.js的同學有一些參考價值。盡量用通俗的語言幫助大家理解,如果有描述或理解不準確的地方歡迎大家指正,交流。另外本文假定你已經對javascript的語法和異步有一些基本的概念。 本系列會按一般學習異步編程的順序,首先介紹一下異步的原理,然后介紹各種異步編程的方法,從回調函數開始,然后慢慢進入Promise和Generator等對異步編程體驗進行改進的技術。期間也會大概提一下事件監聽方式的異步調用,但是因為太多的事件監聽會讓程序流程變的不清晰,所以不太推薦(其實事件模式本質上也是回調函數)。Promise和Generator才是到目前為止(ES6),最好的異步編程方式。后面也會分享一下自己所理解的這兩種方式的對比(網上也有很多爭論)。ES7提出了更好的改進方案,希望很快可以抽出時間研究一下,這是后話:) 好,進入本節的正文。 什么是異步?同步異步與阻塞非阻塞有什么關系? ? node.js的“一切皆異步”的思想很有創意,目的是可以讓開發者輕松編寫高性能的web服務端,而不會“不小心”就用同步api阻塞了服務器從而影響性能。其他的語言比如php, python, java等基于同步的語言,雖然也有異步api,但畢竟編程人員的“思想上是同步的”,有時候不可避免的會寫出阻塞的代碼,node.js的目標是造就“思想上是完全異步”的編程人員和編程語言:) 異步跟同步最大的不同就是異步api或函數被“調用”后不會等它運行結束再執行它后面的代碼,而是調用之后直接往下執行,異步函數的“執行”實際上是放在“其他地方”,待“執行”完成后再把結果通過回調函數來進行進一步的使用或處理(所以異步函數書寫的時候不要用"return"來返回值哦,必須通過回調函數來返回值)。這里為什么強調“調用”和“執行”兩個詞呢?就是為了更好的理解異步的過程。 打個比喻,你的領導要做個電子報表,笨領導的做法是,在自己電腦上把該統計的統計了,該算的算了,最后生成一張報表,然后用這個報表做接下去的工作;聰明的領導,會發個郵件給下屬,把報表的要求寫清楚,下屬在自己的電腦上把報表做完后,發個郵件把報表交回給領導。在下屬做報表的時候,領導可以在自己電腦上繼續做其他事情,比如玩游戲、看視頻等等(你懂的)。 在上面這個例子中,領導是編程者(你),領導的電腦是當前線程,下屬的電腦是另一個線程(如果有多個下屬就相當于有個線程池)。做報表這件工作是個異步函數,發郵件給下屬相當于調用這個函數,下屬電腦上做報表相當于在另一個線程異步執行這個函數,執行完了發郵件把報表發回給領導相當于調用回調函數,領導就可以使用這個報表接著做下面的工作(相當于回掉函數里面的代碼)。下屬做報表的時候領導完全不用管而是可以繼續干其他事情。 通過這個例子可以清楚的看到,領導只能在他自己的電腦(用戶線程)上工作,異步的函數都是在下屬的電腦上(異步線程)做的。這一點在很多文章當中并沒有講的很清楚,所以容易造成困擾,因為很多人只是一味的強調javascript是單線程的,但單線程怎么能實現異步呢?就并沒有講清楚。其實所謂的單線程是指用戶線程是單線程,而另外還有一個或多個線程處理異步代碼的執行。 接下來再說說阻塞的問題。很多文章里面在講解的時候同步異步阻塞非阻塞混為一團,新手很難理解,最容易產生的誤解就是同步=阻塞,異步=非阻塞。其實阻塞非阻塞跟同步異步沒有任何關系。簡單講,阻塞就是一個api或者函數運行時間過長,而獨占cpu導致其他代碼不能運行,那么多長時間算阻塞呢?相信這只是一個相對的概念,只要明顯影響到程序的性能和用戶體驗,就算阻塞吧。那跟同步異步是什么關系呢?阻塞的代碼如果同步執行就會阻塞到自己后面代碼的運行,所以自然而然的就想到異步來執行阻塞的代碼,然而異步真能解決阻塞問題嗎?下面繼續講。 node.js里面的異步 大家知道Javascript的基本語法跟其他編程語言大同小異,最大的不同就是把異步擺在首位,特別是node.js更是大部分api都是異步的,小量同步api。這與其他大部分語言剛好相反,也給習慣于同步編程思維的同學造成了很大的困擾,就是常說的轉不過彎來,習慣性的認為代碼是按順序一行一行的往下執行。 上面介紹了異步非阻塞的概念,那么具體到node.js是怎么實現的呢?這里又會涉及到I/O的概念,具體不講I/O的細節(操作系統原理都會講),關鍵就一點,I/O操作通常比較耗時但不會獨占CPU,典型的I/O比如文件讀寫,遠程數據庫讀寫,網絡請求等。 先講耗時,如果用同步API來進行I/O操作,在返回結果之前就只能等待,所以最好的辦法上面已經講過,就是進行異步操作。接著說一下不會霸占CPU的好處。在node.js進程里面,有一個用戶線程(javascript所宣稱的單線程)和一個異步線程池(用戶無法直接訪問), 如果跑在異步線程上的代碼是阻塞的,那么這種異步根本就起不到消除阻塞的作用,為什么?原因就是阻塞代碼會霸占cpu,導致本進程所有代碼都等待不管是哪個線程。但是,,,剛剛講的node.js里面的I/O API都是不會霸占CPU的,所以是非阻塞的,就不會出現這個問題。這就是node.js的最引以為傲的特性之一:異步非阻塞I/O. 上面只是強調異步非阻塞,那么對于真正的阻塞代碼,node.js怎么辦呢?不好意思。。。單個node.js進程真無能為力,跟同步編程語言相比沒什么優勢,只能用傳統的方式,多進程,多開幾個node.js進程,甚至多開幾個服務器。任何語言都有它的強項和弱項,node.js的強項就是它本來的設計初衷:讓開發者能夠輕松的編寫高性能的web服務器(進行的最多的就是網絡和數據庫的I/O操作),而不是做大量的CPU密集型的運算(不過我趕腳,就算要做很多阻塞操作,用多進程和多服務器又有何不可?node.js對此提供了足夠的支持)。這樣算是回答了上一小節最后的問題了吧:) 要理解javascript異步編程和其他語言同步編程的區別,可以從一個最簡單的例子開始。在同步為主的語言中,如果需要等待10秒鐘,通常是類似sleep 10之類的語句,在10秒之內整個進程掛起,也就是阻塞10秒。但javascript不是這樣,它是使用setTimeout函數,從字面意思就已經可以看出區別了, 設一個timeout的時間, 在這段時間內cpu可以繼續運行其他代碼, 等10秒時間到了, 就用回調函數的形式來做應該10秒之后才做的事情。 到此,這一節異步編程的原理大概就概括了一下,在進一步深入學習異步編程之前一定要理解的概念。以上都是自己的理解,錯誤或不準確的地方還望不吝賜教:) 下一節會介紹最基礎的javascript的異步編程方式:回調函數,所有其他方式都是基于它的改進。 附注:也許是我讀得書少:) 這么久以來沒有發現網上有對異步編程講解的很透徹的文章,在自己學習過的資料當中,樸靈的《深入淺出node.js》是講解的最深入透徹的,強烈推薦。 轉載請標明出處: http://www.cnblogs.com/chrischjh/p/4648395.html
轉載于:https://www.cnblogs.com/chrischjh/p/4648395.html
總結
以上是生活随笔為你收集整理的Javascript异步编程之一异步原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 两金压控的两金是什么
- 下一篇: 查别人的征信怎么查 可以怎么查询他人的征