Node.js基礎(chǔ)入門之回調(diào)函數(shù)及異步與同步詳解
經(jīng)過前面兩天的學(xué)習(xí),已經(jīng)對(duì)Node.js有了一個(gè)初步的認(rèn)識(shí),今天繼續(xù)學(xué)習(xí)其他內(nèi)容,并加以整理分享,如有不足之處,還請(qǐng)指正。
回調(diào)函數(shù)
1. 什么是回調(diào)函數(shù)?
回調(diào)函數(shù),或簡(jiǎn)稱回調(diào)【callback】將一個(gè)A函數(shù)作為參數(shù)傳入另一個(gè)B函數(shù)中,B函數(shù)在執(zhí)行過程中,根據(jù)時(shí)機(jī)或條件決定是否調(diào)用A函數(shù),A函數(shù)就是B函數(shù)的回調(diào)函數(shù)。
2. 回調(diào)函數(shù)實(shí)現(xiàn)機(jī)制
回調(diào)函數(shù)的實(shí)現(xiàn)機(jī)制如下所示:
- 定義一個(gè)回調(diào)函數(shù)(普通函數(shù));
- 將回到函數(shù)的引用地址作為參數(shù)傳遞給調(diào)用者;
- 當(dāng)特定的事件或條件發(fā)生時(shí),調(diào)用者使用函數(shù)指針調(diào)用回調(diào)函數(shù)對(duì)事件進(jìn)行處理。
3. 回調(diào)函數(shù)用途
回調(diào)函數(shù)在JavaScript中使用非常多,最簡(jiǎn)單的場(chǎng)景就是事件注冊(cè)或異步函數(shù)。如:當(dāng)用戶點(diǎn)擊某個(gè)按鈕時(shí),需要做出相應(yīng)的響應(yīng),那么就會(huì)用到回調(diào)函數(shù)。
4. 回調(diào)函數(shù)示例
以常用的setInterval為例,就是將show作為參數(shù)傳遞給setInverval,所以show就是setInterval的回調(diào)函數(shù),如下所示:
function show(){ console.log("今天星期三,又是快樂的一天"); } setInterval(show,1000);
執(zhí)行結(jié)果,如下所示:
關(guān)于setInterval的參數(shù)說明,如下所示:
異步與同步
1. 什么是異步與同步?
同步:一個(gè)任務(wù)等待前一個(gè)任務(wù)結(jié)束,然后再執(zhí)行,程序的執(zhí)行順序與任務(wù)的排列順序是一致的,同步的。
異步:每一個(gè)任務(wù)有一個(gè)或多個(gè)回調(diào)函數(shù)(callback),前一個(gè)任務(wù)結(jié)束后,不是執(zhí)行后一個(gè)任務(wù),而是執(zhí)行回調(diào)函數(shù),后一個(gè)任務(wù)則是不等前一個(gè)任務(wù)結(jié)束就開始執(zhí)行,所以程序的執(zhí)行順序與任務(wù)的排列順序是不一致的,異步的。
2. 同步示例
同步即按順序執(zhí)行,存在先后順序,如下所示:
console.log("1111"); console.log("2222"); console.time("t1"); for(var i=0;i<1000000;i++){ } console.timeEnd("t1"); console.log("3333");
同步執(zhí)行結(jié)果,如下所示:
3. 異步示例一
異步則是采用回調(diào)函數(shù)執(zhí)行,如下所示:
console.log("1111"); console.log("2222"); setTimeout(function(){ console.time("t1"); for(var i=0;i<1000000;i++){ } console.timeEnd("t1"); },1000); console.log("3333");
示例執(zhí)行結(jié)果,如下所示:
4. 異步示例二
即使主線程位于阻塞當(dāng)中,異步回調(diào)函數(shù)也要等待主線程執(zhí)行完成后再執(zhí)行。如下所示:
console.log("1111"); console.log("2222"); setTimeout(function(){ console.log("2222-3333"); },15); console.time("t1"); for(var i=0;i<100000000;i++){ } console.timeEnd("t1"); console.log("3333");
示例執(zhí)行結(jié)果
關(guān)于setTimeOut和setInterval的注意事項(xiàng),如下所示:
- setTimeOut和setInterval兩個(gè)函數(shù)是同步執(zhí)行,但是函數(shù)的回調(diào)函數(shù)參數(shù)是定時(shí)器異步執(zhí)行。
- setTimeOut和setInterval兩個(gè)函數(shù)的最小時(shí)間間隔為10-15ms,即使設(shè)置成0,也是如此。
- 異步函數(shù)的執(zhí)行時(shí)間,永遠(yuǎn)在同步執(zhí)行完之后再執(zhí)行。
關(guān)于主線程和任務(wù)線程的執(zhí)行順序,可參考下圖:
異步的實(shí)現(xiàn)
在Node.js中,異步共有三種實(shí)現(xiàn)方式:
- 回調(diào)函數(shù),回調(diào)函數(shù)不一定是異步,但是異步一定有回調(diào)函數(shù)。
- 事件【基于回調(diào)】
- Promise【ES6新增】
1. 回調(diào)函數(shù)的同步示例
回調(diào)函數(shù)也可能是同步的,如下所示:
console.log("1111"); var arr=[1,2,3,4]; arr.forEach(function(v,i){ console.log(v); }); console.log("2222");
示例執(zhí)行結(jié)果
2. 異步事件示例
定義一個(gè)服務(wù),當(dāng)請(qǐng)求時(shí),返回對(duì)應(yīng)的信息。如下所示:
var http=require("http"); var server=http.createServer(); server.on('request',function(req,res){ res.writeHead(200,{"Content-Type":'text/html;charset=utf-8'}); res.write("<h1>你正在訪問小六子的服務(wù)器</h1>"); res.end(); }); server.listen(8080,function(){ console.log("服務(wù)已啟動(dòng)"); });
3. 異步示例截圖
當(dāng)服務(wù)啟動(dòng)時(shí),如下所示:
當(dāng)發(fā)起請(qǐng)求時(shí),返回信息如下所示:
Promise基礎(chǔ)
1. 什么是Promise ?
Promise(承諾)就是一個(gè)對(duì)象,用來傳遞異步操作的消息。它代表了某個(gè)未來才會(huì)知道的結(jié)果的事件(通常是一個(gè)異步操作),并且這個(gè)事件提供統(tǒng)一的API,可供進(jìn)一步處理。
2. Promise特點(diǎn)
Promise對(duì)象有以下兩個(gè)特點(diǎn):
- 對(duì)象的狀態(tài)不受外界影響。Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):Pending(進(jìn)行中),Resolved(已完成,又稱FulFilled)和Rejected(已失敗)。只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個(gè)狀態(tài)。
- 一旦狀態(tài)改變,就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果。Promise對(duì)象的狀態(tài)改變,只有兩種可能:從Pending變?yōu)镽esolved和從Pending變?yōu)镽ejected。只要這兩種情況發(fā)生,狀態(tài)就不會(huì)再變了,會(huì)一直保持這種結(jié)果。如果改變已經(jīng)發(fā)生了,你再對(duì)Promise對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果。這與事件(Event)完全不同,事件的特點(diǎn)是,如果你錯(cuò)過了它,再去監(jiān)聽,是得不到結(jié)果的。
3. 異步的缺點(diǎn)
異步的執(zhí)行順序和時(shí)間是不可控的,如下所示:
假如現(xiàn)在有兩個(gè)文件file1.txt,file2.txt,如下所示:
這兩個(gè)文件是有先后順序的,然后依次進(jìn)行讀取,代碼如下所示:
var fs =require("fs"); fs.readFile("./file1.txt",function(err,data){ console.log(data.toString()); }); fs.readFile("./file2.txt",function(err,data){ console.log(data.toString()); });
示例結(jié)果如下所示:
通過以上示例不難發(fā)現(xiàn),每次運(yùn)行得到的結(jié)果不完全相同,有時(shí)與我們預(yù)期的結(jié)果并不一致,這就是異步的不可控性。那么如何解決呢?
4. Promise保證異步順序
通過Promise可以保證異步執(zhí)行的順序,如下所示:
var p1 = new Promise(function(resolve,reject){ fs.readFile("./file1.txt",function(err,data){ if(err){ reject(err); }else{ resolve(data.toString()); } }); }); var p2 = new Promise(function(resolve,reject){ fs.readFile("./file2.txt",function(err,data){ if(err){ reject(err); }else{ resolve(data.toString()); } }); }); //通過數(shù)組中的順序,控制異步輸出的順序 Promise.all([p1,p2]).then(function(datas){ console.log(datas); },function(errs){ console.log(errs); });
優(yōu)化后的結(jié)果,如下所示:
通過以上示例發(fā)現(xiàn),Promise可以通過消息的傳遞,保證異步操作的順序。
到此這篇關(guān)于Node.js基礎(chǔ)入門之回調(diào)函數(shù)及異步與同步詳解的文章就介紹到這了,更多相關(guān)Node.js回調(diào)函數(shù) 同步異步內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用 Node.js 對(duì)文本內(nèi)容分詞和關(guān)鍵詞抽取
這篇文章主要介紹了使用 Node.js 對(duì)文本內(nèi)容分詞和關(guān)鍵詞抽取,需要的朋友可以參考下2017-05-05NodeJs讀取JSON文件格式化時(shí)的注意事項(xiàng)
本文是作者在進(jìn)行NodeJs開發(fā)時(shí)偶然發(fā)現(xiàn)的問題,經(jīng)過一番努力,最終找到解決方案,分享給大家,有需要的小伙伴可以參考下2016-09-09通過實(shí)例了解Nodejs模塊系統(tǒng)及require機(jī)制
這篇文章主要介紹了通過實(shí)例了解Nodejs模塊系統(tǒng)及require機(jī)制,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07electron demo項(xiàng)目npm install安裝失敗的解決方法
下面小編就為大家分享一篇electron demo項(xiàng)目npm install安裝失敗的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02Node在Controller層進(jìn)行數(shù)據(jù)校驗(yàn)的過程詳解
這篇文章主要給大家介紹了關(guān)于Node在Controller層進(jìn)行數(shù)據(jù)校驗(yàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08