產(chǎn)生問(wèn)題的經(jīng)過(guò)
手頭有個(gè)項(xiàng)目,遇到一個(gè)問(wèn)題,nodejs中通過(guò)執(zhí)行shell命令來(lái)執(zhí)行本地程序,我希望是先執(zhí)行本地程序,得到返回結(jié)果,做出if判斷。
所以我將執(zhí)行本地程序的代碼放在了if判斷的前面,在我的印象中nodejs基于js,js是單線程,同步執(zhí)行,所以這段程序一定是按順序執(zhí)行。但結(jié)果是得不到返回結(jié)果總出錯(cuò)。
解決問(wèn)題
問(wèn)題得到解決,兩步:
1.查看官方文檔,由于我做的是火狐擴(kuò)展,我參考的是MDN官方文檔
2.調(diào)試,不斷地調(diào)試,動(dòng)手比空想強(qiáng)。
這里執(zhí)行本地程序時(shí)間長(zhǎng),nodejs這里是異步執(zhí)行,所以還沒(méi)得到返回結(jié)果就進(jìn)行了判斷。
解決的辦法是把判斷程序放在了child_process.exec('shell', callback)中的回調(diào)函數(shù)callback中。(其實(shí)nodejs官方文檔中支持execSync直接支持同步執(zhí)行,可惜火狐擴(kuò)展中不支持這個(gè)方法。)
新的問(wèn)題
1.nodejs執(zhí)行異步的原理,js是否能執(zhí)行異步,單線程如何實(shí)現(xiàn)異步?
2.回調(diào)函數(shù)是什么,如何利用它執(zhí)行異步?
解決新的問(wèn)題
1.nodejs的單線程執(zhí)行實(shí)際上只是針對(duì)我們所寫(xiě)的js代碼而言的,它的底層實(shí)際上用的是c/c++,遇到I/O這樣的耗時(shí)量大的底層會(huì)開(kāi)啟一個(gè)線程執(zhí)行一個(gè)回調(diào)函數(shù)。舉個(gè)栗子。上一幅圖具體來(lái)說(shuō),當(dāng)我們調(diào)用 fs.open 時(shí),Node.js 通過(guò) process.binding 調(diào)用 C/C++ 層面的 Open 函數(shù),然后通過(guò)它調(diào)用 Libuv 中的具體方法 uv_fs_open,最后執(zhí)行的結(jié)果通過(guò)回調(diào)的方式傳回,完成流程。Libuv就是用c++編寫(xiě)的底層。
而這種異步模式下,實(shí)現(xiàn)同步,就可以用以上提到的回調(diào)函數(shù)的方法來(lái)寫(xiě)同步。文章參考1,文章參考2.
2.js是毫無(wú)疑問(wèn)的單線程,同一時(shí)間段只能完成一件事情,但這并不代表它沒(méi)有辦法實(shí)現(xiàn)異步,異步和單線程并沒(méi)有直接的關(guān)系,實(shí)際上有好幾種方法能夠?qū)崿F(xiàn),可以參考文章。
3.回調(diào)函數(shù)是指是指通過(guò)函數(shù)參數(shù)傳遞到其它代碼的,某一塊可執(zhí)行代碼的引用。即可以簡(jiǎn)單理解為是函數(shù)被當(dāng)作參數(shù)傳入另外一個(gè)函數(shù)當(dāng)中,并在那個(gè)函數(shù)中被調(diào)用。
從以上也可以看到,回調(diào)只是一種執(zhí)行方法,既有異步回調(diào),也有同步回調(diào)。舉個(gè)簡(jiǎn)單栗子:
var b = function (){
//執(zhí)行相關(guān)的代碼
}
var a = function (b){
//執(zhí)行相關(guān)的代碼
b();
}
a(b);
這是同步回調(diào)。
如果f1是一個(gè)很耗時(shí)的任務(wù),f2需要f1的結(jié)果執(zhí)行,可以把它們寫(xiě)成
function f1(callback){
setTimeout(function () {
// f1的任務(wù)代碼
callback();
}, 1000);
}
f1(f2);
這樣js會(huì)先執(zhí)行其他的主要邏輯,js線程空閑一秒之后再執(zhí)行這個(gè)操作。從而實(shí)現(xiàn)異步。
文章參考
總結(jié)
寫(xiě)下這篇文章的過(guò)程其實(shí)也是我弄清楚其中原委的過(guò)程,這只是一個(gè)很細(xì)節(jié)的問(wèn)題,但是思考的過(guò)程卻十分漫長(zhǎng)。共勉。