1.1同步API夺溢,異步API
當(dāng)中的API:有的通過返回值拿到結(jié)果,有的通過函數(shù)返回值方式拿到結(jié)果为流。
例如:
// 返回值拿到
// 路徑拼接
const public = path.join(__dirname,'public');
// 請(qǐng)求地址解析
const urlObj = url.parse(req.url);
// 函數(shù)返回值
// 讀取文件
fs.readFile('./demo.txt,'utf8',(err,result)=>{
console.log(result);
});
為什么會(huì)有這兩種方式酸茴?
===============================.
什么是同步API?
只有當(dāng)前API執(zhí)行完成后沼本,才能繼續(xù)執(zhí)行下一個(gè)API
例如:
console.log('before');
console.log('after');
// 只有第一個(gè)執(zhí)行完噩峦,才能執(zhí)行第二個(gè)。這就是同步API抽兆,代碼從上到下一行一行的執(zhí)行识补,只有上一行代碼執(zhí)行完成,才能執(zhí)行下一行代碼郊丛。
什么是異步API李请?
當(dāng)前API的執(zhí)行瞧筛,不會(huì)阻塞后面代碼的執(zhí)行!
例如:
console.log('before');
setTimeout(
()=>{console.log('last');
},2000)
console.log('after');
// 結(jié)果:先輸出before,再輸出after,2s后輸出了last导盅,(PS:雖然定時(shí)器代碼在after前面较幌,但是程序在執(zhí)行到定時(shí)器代碼時(shí),并沒有等待定時(shí)器代碼執(zhí)行完成白翻,再執(zhí)行after這個(gè)方法乍炉,在當(dāng)前代碼中定時(shí)器就是異步API,程序不需要等待異步API執(zhí)行完成之后滤馍,再繼續(xù)執(zhí)行后面的代碼岛琼,也就是說異步API不會(huì)阻塞后續(xù)代碼的執(zhí)行。)
在Node.js中異步API無(wú)處不在巢株,掌握異步異步API槐瑞,異步編程非常重要。
1.2同步API阁苞,異步API的區(qū)別(獲取返回值)
同步API可以從返回值中拿到API執(zhí)行的結(jié)果困檩,但是異步API不可以。
例子:
// 同步
function sum (n1 ,n2){
return n1 + n2;
}
const result = sum(10,20);
// 異步
function getMsg(){
setTimeout(function(){
return{msg:'Hello Node,js'}
},2000);
//return undefined
}
const msg = getMsg();
console.log(msg);
思考:是否能拿到函數(shù)返回值那槽?
執(zhí)行后返回: undefind
PS: 原因:當(dāng)使用函數(shù)名getMsg函數(shù)名去調(diào)用getMsg()函數(shù)時(shí)悼沿,由于異步API不會(huì)阻塞代碼的執(zhí)行,所以在函數(shù)的底部直接return undefind(PS:因?yàn)闆]有寫return骚灸,所以他默認(rèn)返回的就是undefined,然后再過2秒后糟趾,他在setTimeout函數(shù)內(nèi)部返回了一個(gè)對(duì)象,此時(shí)返回值早已拿到了undefined,然后直接進(jìn)行了輸出)
結(jié)論:在異步API中甚牲,無(wú)法通過返回值的方式去拿到異步API的執(zhí)行結(jié)果义郑。
1.3回調(diào)函數(shù)
那么異步API的返回值到底該怎么拿到?
實(shí)際上是通過回調(diào)函數(shù)拿到的,什么是回調(diào)函數(shù)鳖藕,實(shí)際上就是自己定義函數(shù)魔慷,讓別人去調(diào)用函數(shù)(看下面例子))
// getData函數(shù)定義
function getData(callback){}
//getData函數(shù)調(diào)用
getData(()=>{});
// callback僅僅是形參的名字,那么這個(gè)形參對(duì)應(yīng)的實(shí)參實(shí)際上是一個(gè)函數(shù)著恩,將一個(gè)函數(shù)作為另外一個(gè)函數(shù)的參數(shù)(沒有問題)院尔,callback就是一個(gè)回調(diào)函數(shù),在調(diào)用getData的時(shí)候傳遞了一個(gè)匿名函數(shù)(函數(shù)的定義喉誊,實(shí)際上這個(gè)函數(shù)在getData方法的內(nèi)部調(diào)用邀摆,根據(jù)實(shí)際的情況決定是否要調(diào)用,來(lái)決定什么是要調(diào)用伍茄,這個(gè)參數(shù)就是回到函數(shù))栋盹。
在代碼編輯器中新建一個(gè)文件callback.js
function getData(callback){
callback();
}
getData(function(){
console.log('callback函數(shù)被調(diào)用了')
});
命令行工具執(zhí)行node callback.js
返回:callback函數(shù)被調(diào)用了
// 這就是回調(diào)函數(shù)其中一種表現(xiàn)形式
函數(shù)寫成這個(gè)樣子有什么用?
function getData(callback){
callback('123'); // 給callback傳遞參數(shù) 123
}
getData(function(n){ // 在這個(gè)函數(shù)里拿到123這個(gè)實(shí)參敷矫,使用形參n對(duì)象這個(gè)實(shí)參
console.log('callback函數(shù)被調(diào)用了')
console.log(n)例获;
});
返回:callback函數(shù)被調(diào)用了
123
PS:123被輸出了妇多。如果getData這個(gè)函數(shù)在內(nèi)部有異步操作鲸郊,在異步操作執(zhí)行完成之后古今,就可以調(diào)用callback('123')這個(gè)回調(diào)函數(shù)轮蜕,并且把異步API執(zhí)行的結(jié)果通過參數(shù)的形式傳遞給callback,那么我們?cè)趃etData里面的回調(diào)函數(shù)里面就能夠拿到異步API執(zhí)行的結(jié)果收壕。
回到第一個(gè)例子:
function getMsg(callback){
setTimeout(function(){
callback({
msg:'hello node.js'
})
},2000);
}
getMsg(function(data){
console.log(data);
});
命令行執(zhí)行
返回:{mag:'hello node.js'}
PS:通過回調(diào)函數(shù)的方式拿到了異步API執(zhí)行的結(jié)果妓灌。
1.4同步API,異步API的區(qū)別(代碼執(zhí)行順序)
同步API從上到下一次執(zhí)行蜜宪,前面代碼沒有執(zhí)行完成之前虫埂,后面的代碼不能執(zhí)行
for(var i=0;i<100000;i++){
console.log(i);
}
console.log('for循環(huán)后面的代碼');
PS:先執(zhí)行for循環(huán)10萬(wàn)次圃验,在for循環(huán)沒有執(zhí)行完成之前掉伏,后面的console.log()是不能執(zhí)行的。
異步API不會(huì)等待API執(zhí)行完成后再向下執(zhí)行
console.log('代碼開始執(zhí)行');
setTiomeout(()=>{console.log('2S后執(zhí)行的代碼')}损谦,2000)岖免;
setTiomeout(()=>{console.log('0S后執(zhí)行的代碼')},0)照捡;
console.log('代碼結(jié)束執(zhí)行');
返回:代碼開始執(zhí)行
代碼結(jié)束執(zhí)行
0S后執(zhí)行的代碼
2S后執(zhí)行的代碼
總結(jié):同步代碼執(zhí)行區(qū)→異步代碼執(zhí)行區(qū)→回調(diào)函數(shù)隊(duì)列
同步執(zhí)行完成后,再異步代碼執(zhí)行區(qū)執(zhí)行(完成后)话侧,再去回調(diào)函數(shù)隊(duì)列栗精,再將隊(duì)列中的回調(diào)函數(shù)放入同步代碼執(zhí)行區(qū)執(zhí)行。