Node.js 耗時操作async調用阻塞原因詳解


問題背景:

在公司的一個Node.js項目中,在async方法內部,需要調用另外一個async方法進行大文本的正則匹配(耗時非常久),之前以為只要是不添加await關鍵字,這個方法就可以自動的異步進行調用。但實際上,每當訪問這個接口時,響應還是非常的緩慢。

Node的事件模型

如上圖所示,每個請求到Node的時候,程序會把請求方法與其它方法放入一個事件隊列中,然后在Node的主線程中重復循環處理函數,當遇到阻塞時(一般是磁盤IO、數據庫IO、網絡IO等),主線程會跳到下一個函數執行。而那個等待IO的函數則由線程池進行監聽,當子線程監聽到完成事件,就把該回調函數放入事件循環隊列中,讓主線程進行回調。

問題的理解與解答

Promise的認知

上面提到了,以不添加await的形式調用async方法。實際上,await和async是對Promise的一個語法糖的體現,其中用async聲明的函數本身就是一個Promise對象,而在調用時添加await方法大致等同於在Promise后面添加一個then()的回調函數來處理Promise的結果。

解答

根據上面的描述,只要調用async不添加await關鍵字的方法,那么這個方法就會異步的執行,而本次事件的流程會繼續往下走。那么,為什么在調用非常耗時的操作后,整個應用程序會變得異常緩慢呢?

我是這么理解的:Promise只是一個異步方法的容器,僅僅標明這個方法是異步的、一定會有結果的。但是它並不會調用線程池的線程去執行這個Promise,總的來說就是:這個Promise是由Node的主線程去執行的。這就會造成一個問題,即便當前調用耗時操作的方法沒有因此阻塞,但是后續事件循環到這個耗時操作時,Node主線程還是得去處理,一旦線程處理這個耗時操作,那么整個應用程序的其它請求就無法得到響應。

實際解決方式

根據上述描述,我們需要一個真正的異步線程去執行我們的耗時函數,這個時候可以用到的模塊有:

Worker threads, child processes, clusters, job queues

具體的實現可以根據Node的api文檔進行。

此外,如果還不太理解上述內容,可以訪問下面幾條鏈接進一步了解:

這兩個是問答,有非常直接的答案:

https://stackoverflow.com/questions/46004290/will-async-await-block-a-thread-node-js

https://www.reddit.com/r/node/comments/a5a7fe/how_to_do_work_asynchronously/

介紹耗時操作如何阻塞整個事件循環,雖然和這個問題不太相關,但是寫的還不錯:

https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM