5. Node.js異步編程
5.1 同步API,異步API
// 路徑拼接
const public =path.join(__dirname,'public');
// 請求地址解析
const urlObj =url.parse(req.url);
// 讀取文件
fs.readFile('./demo.txt','utf8',(err,result) => {
console.log(result);
});
同步API:只有當(dāng)前API執(zhí)行完成后往声,才能繼續(xù)執(zhí)行下一個API
console.log('before');
console.log('after');
//before
//last
異步API:當(dāng)前API的執(zhí)行不會阻塞后續(xù)代碼的執(zhí)行
console.log('before');
setTimeout(
()=> {console.log('last');
}, 2000);
console.log('after');
//before
//after
//last
5.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);
}
const msg =getMsg ();//undefinded
5.3 回調(diào)函數(shù)
自己定義函數(shù)讓別人去調(diào)用窿克。
//getData函數(shù)定義
function getData (callback){}
// getData函數(shù)調(diào)用
getData(()=> {});
5.4 使用回調(diào)函數(shù)獲取異步API執(zhí)行結(jié)果
function getMsg (callback){
setTimeout(function () {
callback({ msg:'Hello Node.js'})
}, 2000);
}
getMsg (function(msg){
console.log(msg);//{ msg:'Hello Node.js'}
});
5.5 同步API,異步API的區(qū)別(代碼執(zhí)行順序)
同步API從上到下依次執(zhí)行朱盐,前面代碼會阻塞后面代碼的執(zhí)行
for (var i = 0;i < 100000;i++) {
console.log(i);
}
console.log('for循環(huán)后面的代碼');
異步API不會等待API執(zhí)行完成后再向下執(zhí)行代碼
console.log('代碼開始執(zhí)行');
setTimeout(() => {console.log('2秒后執(zhí)行的代碼')},2000);
setTimeout(() => {console.log('"0秒"后執(zhí)行的代碼')},0);
console.log('代碼結(jié)束執(zhí)行');
//代碼開始執(zhí)行
//代碼結(jié)束執(zhí)行
//"0秒"后執(zhí)行的代碼
//2秒后執(zhí)行的代碼
5.6 代碼執(zhí)行順序分析
console.log('代碼開始執(zhí)行');
setTimeout(() => {
console.log('2秒后執(zhí)行的代碼');
}, 2000);
setTimeout(() => {
console.log('"0秒"后執(zhí)行的代碼');
}, 0);
console.log('代碼結(jié)束執(zhí)行');
5.7 Node.js中的異步API
fs.readFile('./demo.txt',(err,result) => {});
var server =http.createServer();
server.on('request',(req,res) => {});
如果異步API后面代碼的執(zhí)行依賴當(dāng)前異步API的執(zhí)行結(jié)果薛训,但實際上后續(xù)代碼在執(zhí)行的時候異步API還沒有返回結(jié)果苍匆,這個問題要怎么解決呢罢维?
fs.readFile('./demo.txt',(err,result) => {});
console.log('文件讀取結(jié)果');
需求:依次讀取A文件、B文件绢记、C文件
const fs=require('fs');
//回調(diào)地獄
fs.readFile('./1.txt','utf8',(err,result1)=>{
console.log(result1);
fs.readFile('./2.txt','utf8',(err,result2)=>{
console(result2);
fs.readFile('./3.txt','utf8',(err,result3)=>{
console(result3);
});
});
});
5.8 Promise
Promise出現(xiàn)的目的是解決Node.js異步編程中回調(diào)地獄的問題。
let promise =new Promise((resolve,reject) => {
setTimeout(() => {
if(true){
resolve({name:'張三'})//成功讀取執(zhí)行
}else {
reject('失敗了')//失敗讀取執(zhí)行
}
}, 2000);
});
promise.then(result=> console.log(result)); // {name: '張三'}//resolve傳參進入then執(zhí)行回調(diào)函數(shù)
.catch(error => console.log(error)); // 失敗了//reject傳參進入catch執(zhí)行回調(diào)函數(shù)
5.9 異步函數(shù)
異步函數(shù)是異步編程語法的終極解決方案正卧,它可以讓我們將異步代碼寫成同步的形式蠢熄,讓代碼不再有回調(diào)函數(shù)嵌套,使代碼變得清晰明了炉旷。
const fn =async () => {};
async function fn () {}
async關(guān)鍵字
1. 普通函數(shù)定義前加async關(guān)鍵字 普通函數(shù)變成異步函數(shù)
2. 異步函數(shù)默認(rèn)返回promise對象
3. 在異步函數(shù)內(nèi)部使用return關(guān)鍵字進行結(jié)果返回 結(jié)果會被包裹的promise對象中 return關(guān)鍵字代替了resolve方法
4. 在異步函數(shù)內(nèi)部使用throw關(guān)鍵字拋出程序異常
5. 調(diào)用異步函數(shù)再鏈?zhǔn)秸{(diào)用then方法獲取異步函數(shù)執(zhí)行結(jié)果
6. 調(diào)用異步函數(shù)再鏈?zhǔn)秸{(diào)用catch方法獲取異步函數(shù)執(zhí)行的錯誤信息
await關(guān)鍵字
1. await關(guān)鍵字只能出現(xiàn)在異步函數(shù)中
2. await promise await后面只能寫promise對象 寫其他類型的API是不不可以的
3. await關(guān)鍵字可是暫停異步函數(shù)向下執(zhí)行直到promise返回結(jié)果
const fs = require('fs');
// 改造現(xiàn)有異步函數(shù)api 讓其返回promise對象 從而支持異步函數(shù)語法
const promisify = require('util').promisify;
// 調(diào)用promisify方法改造現(xiàn)有異步API 讓其返回promise對象
const readFile = promisify(fs.readFile);
async function run () {
let r1 = await readFile('./1.txt', 'utf8')
let r2 = await readFile('./2.txt', 'utf8')
let r3 = await readFile('./3.txt', 'utf8')
console.log(r1)
console.log(r2)
console.log(r3)
}
run();
//1
//2
//3