Node.js中同步和異步編程的區(qū)別及使用方法
一、進程和線程
代碼編寫完畢在編譯的過程中計算機的內(nèi)存中會開辟一個空間來存儲代碼,這個空間就相當于是進程,可以將進程類比于工廠的廠房,但代碼相當于原材料,但僅有廠房和原材料無法生產(chǎn),還需要工人進行加工,工人則類比于線程
- 線程(廠房):程序運行的環(huán)境
- 線程(工人):線程
二、同步和異步
程序的運行分為同步和異步兩種;
同步
同步指的是事情按照順序執(zhí)行;例如早上起床,我們先刷牙,再洗臉,最后吃早餐,講究的是事情先后的執(zhí)行順序
通常情況下我們的程序時按照同步執(zhí)行的,即按照順序一條一條執(zhí)行,一條執(zhí)行完畢執(zhí)行下一條,如下所示:
/* 同步 - 通常情況下代碼都是自上而下的 */ console.log("哈哈") console.log("嘻嘻") console.log("嘿嘿")
通過快捷鍵F5通過NodeJs在控制臺中打印輸出內(nèi)容為以上順序執(zhí)行的結(jié)果
哈哈
嘻嘻
嘿嘿
下面的代碼示例就是非常典型的同步結(jié)構(gòu)的代碼:
function sum(a, b){ return a + b } console.log("第一行打印") let result = sum(123, 234) console.log(result) console.log("第二行打印")
同步的特點是邏輯清晰、結(jié)構(gòu)簡單、容易理解;
同步:
- 通常情況下代碼都是自上而下一行一行執(zhí)行的
- 前邊的代碼不執(zhí)行后邊的代碼也不會執(zhí)行
- 同步的代碼執(zhí)行會出現(xiàn)阻塞的情況
- 一行代碼執(zhí)行慢會影響到整個代碼的執(zhí)行
解決同步的問題:
java python
- 通過多線程來解決
node.js
- 通過異步的方式來解決
阻塞
同步最大的問題就是阻塞;
所謂的阻塞就是按順序執(zhí)行的代碼,前面不執(zhí)行完畢后面的代碼不會執(zhí)行;
function sum(a, b){ let begin = Date.now() while(Date.now() - begin < 10000){ } return a + b } console.log("第一行打印") let result = sum(123, 234) console.log(result) console.log("第二行打印")
上述sum()執(zhí)行的時候會停頓10秒,10秒后才會返回結(jié)果,由于是同步執(zhí)行代碼,所以sum()會阻礙后面所有代碼的執(zhí)行,導致整個程序的執(zhí)行速度變慢
異步
- 一段代碼的執(zhí)行不會影響到其他程序
- 異步的問題:
- 異步的代碼無法通過return來設(shè)置返回值
- 特點:
1. 不會阻塞其他代碼的執(zhí)行
2. 需要通過回調(diào)函數(shù)來返回結(jié)果
- 基于回調(diào)函數(shù)的異步帶來的問題
1. 代碼的可讀性差
2. 可調(diào)試性差
- 解決問題:
- 需要一個東西,可以代替回調(diào)函數(shù)來給我們返回結(jié)果
- Promise是一個可以用來存儲數(shù)據(jù)的對象
Promise存儲數(shù)據(jù)的方式比較特殊;
這種特殊的方式使得Promise可以用來存儲異步調(diào)用的數(shù)據(jù)
在程序中,有些代碼的執(zhí)行速度很快,比如說:打印一個內(nèi)容、接受一個請求、發(fā)送一個響應這些都是簡單且能快速反應的操作,但例如:讀寫硬盤中的文件(I/O)操作這些就會非常的慢,如果在這些非常耗時的操作影響到能快速反應的操作,這是我們不希望看到的
對于其他語言,例如java,處理這類問題的方式簡單粗暴,就是直接開啟多線程,每一個線程處理一個事務,避免相互干擾;
但是對于Node.js來說,它本身就是單線程的,那么如何處理這種情況,答案就是采用異步;當我們?nèi)プx取一個比較大的文件時,node先將指令發(fā)給計算機,然后再由計算機去讀取文件,此時node的線程不是等待計算機數(shù)據(jù)返回,而是繼續(xù)向下執(zhí)行其他的操作,那么何時去獲取計算機讀取到的數(shù)據(jù)呢?等數(shù)據(jù)返回了我們再去讀取,這樣既不影響其他操作也可以正常的讀取到計算機上返回的數(shù)據(jù);
function sum(a, b){ setTimeout(()=>{ return a + b }, 10000) } console.log("第一行打印") let result = sum(123, 234) console.log(result) console.log("第二行打印")
上述代碼中,我們將計算操作放到了setTimeout中,同樣是等待10s,但是setTimeout不會阻塞其他代碼的執(zhí)行,而是在10s之后將函數(shù)放到了任務隊列,這樣一來就可以很好的解決阻塞問題。
但由于函數(shù)的返回值return到了setTimeout的回調(diào)函數(shù)中,此時我們再調(diào)用sum() 就無法獲取到函數(shù)的計算結(jié)果了,因此后面調(diào)用sum()傳入實參的時候計算結(jié)果為undefined;
那么如何獲取異步代碼的執(zhí)行結(jié)果呢,只有通過回調(diào)函數(shù)了,異步代碼通常都需要一個回調(diào)函數(shù)作為參數(shù),當異步代碼執(zhí)行完畢取得結(jié)果時候便可以將結(jié)果作為回調(diào)函數(shù)的參數(shù)進行傳遞,這樣我們便可以在回調(diào)函數(shù)中來讀取結(jié)果,并完成后續(xù)操作
// 回調(diào)函數(shù):將函數(shù)作為參數(shù)傳遞 function sum(a, b, cb){ setTimeout(()=>{ cb(a + b) }, 10000) } console.log("第一行打印") sum(123, 234, result => { console.log(result) }) console.log("第二行打印")
問題
異步通過回調(diào)函數(shù)解決運算結(jié)果的傳遞問題,但最大的問題也來自于回調(diào)函數(shù),由于是異步執(zhí)行,回調(diào)函數(shù)無法直接通過返回值來返回執(zhí)行結(jié)果,想要取得結(jié)果必須通過回調(diào)函數(shù),
這樣就帶來一個問題:如果我們有兩個異步操作需要先后執(zhí)行,一個異步操作依賴于上一個異步操作的結(jié)果,那么我們只能采取嵌套措施了
// 回調(diào)函數(shù):將函數(shù)作為參數(shù)傳遞 function sum(a, b, cb){ setTimeout(()=>{ cb(a + b) }, 10000) } console.log("11111") sum(123, 234, result => { // 計算777與前兩個數(shù)的加和 sum(result, 7, result => { // 拿結(jié)果再進行求和操作 sum(result, 8, result => { sum(result, 9, result => { }) }) }) })
上述的示例中,調(diào)用了4次sum,每一次都調(diào)用了之前的計算結(jié)果,后一個是依賴前運算結(jié)果的;
這樣子就是“回調(diào)地獄",又名死亡金字塔,這還只是4次,現(xiàn)實的代碼可能比這更加復雜;
總之,異步提高了代碼運行的效率,同樣也增加了代碼的復雜程度;
到此這篇關(guān)于Node.js中同步和異步編程的區(qū)別及使用方法的文章就介紹到這了,更多相關(guān)Node.js中同步和異步內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
nodejs環(huán)境使用Typeorm連接查詢Oracle數(shù)據(jù)
這篇文章主要介紹了nodejs環(huán)境使用Typeorm連接查詢Oracle數(shù)據(jù),本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12Node.js實現(xiàn)爬取網(wǎng)站圖片的示例代碼
本文將利用Node.js開發(fā)一個小示例—爬取某圖片網(wǎng)站的圖片,文中涉及的知識點有https模塊、cheerio模塊、fs模塊和閉包,感興趣的可以了解一下2022-04-04