node.js异步工作主要涉及的如何使得代码执行更加同步
node.js是基于事件驱动的,它是非阻塞的,所以在 网络请求/数据读写 等
方面都是异步的,而这些在node.js中又有对应的核心模块,比如http/file等
那么如何解决代码执行更加同步化?node.js提供了以下4中解决方案:
1.回调函数
2.es6 的promise,高级异步解决方案,万物皆可promise
3.基于promise的 async/await
4.事件驱动
当然,除上面四种解决方案之外,还涉及定时器方面(setTimeOut/setInterval)
回调函数
异步回调控制,当解决完成某个事件时的操作,容易导致回调地狱问题
fs.readFile('file1.txt', function(err, data) {
// do something
fs.readFile('file2.txt', function(err, data) {
// do something
});
});
/**
* 本示例代码中使用了回调函数的概念
*
* 函数在js中是一等公民,可以作为参数传递,也可以作为其它参数的返回值
*
* 本示例中,callback函数就是一个回调函数并且是一个匿名函数,接受result以及status为返回值
*
* 回调函数在很多应用中得到应用,比如array数组的map方法,比如setTimeOut等等
* */
function final(someInput, callback) {
console.log('final')
callback(`${someInput} and terminated by executing callback `,200);
}
function middleware(someInput, callback) {
console.log('middleware')
return final(`${someInput} touched by middleware `, callback);
}
function initiate() {
const someInput = 'hello this is a function ';
middleware(someInput, function (result,status) {
console.log(result);
console.log("status:"+status)
// requires callback to `return` result
});
}
initiate();
通过官方map函数理解回调函数:
/**
*本示例为map中的callback函数调用
*
* 实现自己的myMap callback函数
*
* */
const numbers=[1,2,3,4,5]
const squares=numbers.map(function (num,index,array) {
console.log(num+"=>"+index+"=>"+array)
return num*num
})
console.log(squares)
/**
* myMap写法和官方map类似
* */
Array.prototype.myMap=function (callback) {//callback作为函数进行传递
if(typeof callback !=='function'){
throw new TypeError(`${callback} is not function`)
}
let newArray=[]
//遍历
for(let i=0;i<this.length;i++){
newArray.push(callback(this[i],i,this))
}
//返回新数组
return newArray
}
const myMapNumbers=numbers.myMap(function (num,index,array) {
console.log(num+"=>"+index+"=>"+array)
return num*num
})
console.log(myMapNumbers)
promise
promise是由es6提出,作用就是解决回调地狱,使得代码更加简洁可读,记住一点,万物 皆可promise
promise 分为三种状态,pendging,fulfilled,rejected,对于一个promise而言
如果没有进行resolve或者rejected,那么它是还没有结束的(一般配合async/await使用)
/**
* 在es6中引入了 promise,promise是一种更高级的异步控制,能够解决回调地狱问题
*
* promise具有三个状态 pending(等待) fulfilled(完成) rejected(拒绝)
*
* 作为es6更高级的异步解决方案,万物皆可promise,只需要将其封装为promise即可
*
* promise的链式操作让代码层次化清晰
*
*
* */
import fs from 'node:fs'
fs.readFile('./test.txt',function (err,data) {
if(err){
throw err
}
console.log(data.toString())
})
/**
* 使用promise解决异步到同步编程
* */
const newPromise= new Promise(function (resolve, reject) {
fs.readFile('./test.txt',function (err,data) {
if(err){
reject(err)
}
resolve(data)
})
})
newPromise.then(res=>{
console.log(0)
console.log(res.toString())
}).then(res=>{
console.log(1)
}).then(res=>{
console.log(2)
}).catch(err=>{
throw err
})
console.log('-----------------------------------------------')
/**
*
* 对于promise而言,只有rejected或者fulfilled状态时,才算状态完成,即resolve或者reject,
*
* 如果没有进行resolve或者reject,那么这个promise并没有进行结束,
* 对于浏览器而言,因为界面一直打开,那么它会达到类似同步效果(需要使用await)
*
* 对于node.js环境而言,因为它是非阻塞的,如果没有i/o操作或者定时器,那么它是会直接结束的
* */
let customResolve=null
async function test() {
return await new Promise(function (resolve, reject) {
console.log('hello')
customResolve=resolve
})
}
async function test1() {
let data=await test()//阻塞
console.log('data:'+data)
}
function test2() {
console.log('teste2')
customResolve('confirm')
}
test1()
test2()
async/await
async/await 是基于promise的,因为await之后就是获取对应promise接受的值,
而对于await作用于普通函数,实际上也是将函数返回的数据包装为一个promise
接受的值
/**
*
* node.js在异步控制中除了通过回调函数进行解决,还通过async/await
*
* async/await 是一种基于promise更高级的方式,使得代码看起来更加同步,所以本质上出现是以解决异步promise
* 当await作用于promise时,返回得到的是已解决promise的值(不管是reject还是resolve)
* 当作用于普通函数时,js将会将函数返回值包装为已解决的promise的值
*
* await表示等待执行其后函数,只有执行完成才执行下一步
*
*
* */
import fs from 'node:fs'
const readFilePromise=function (path){
return new Promise(function (resolve, reject) {
fs.readFile(path,function (err,data) {
if (err){
reject(err)
}
resolve(data)
})
})
}
async function mode() {
return 1234
}
async function readFile() {
let data1=await readFilePromise('./test.txt')//作用于promise
console.log(data1.toString())
let data2=await mode()//作用于普通函数
console.log(data2)
}
readFile()
事件驱动
node.js本身基于事件驱动,所以很多事情可以通过模拟进行
/**
*
* EventEmitter
*
* node.js 是基于事件驱动的单线程模型,这种模型是非阻塞的
* 比如 文件读取/网络请求/数据查询 等,其中这些都有对应的模块,也是node.js的核心模块
*
* once 监听一次
*
* removeListener('event3',listener1) 删除某一个事件监听器,两个参数
*
* removeAllListeners 删除所有事件监听器
*
* */
import events from 'node:events'
const emitter = new events.EventEmitter();
emitter.on('event1', function() {
// do something
emitter.emit('event2','这是参数')
console.log(11)
});
// emitter.once('event1', function() {//监听一次
// // do something
// emitter.emit('event2','这是参数')
// console.log(11)
// });
emitter.on('event2',function (param1) {
console.log(22+param1)
})
emitter.emit('event1');
// emitter.removeListener('event1')//删除
emitter.removeAllListeners()//删除所有,在这之后使用的emit都输出为空,不会报错
emitter.emit('event1');
console.log('----------------------------------')
function listener1() {
console.log('listener1 triggered');
}
function listener2() {
console.log('listener2 triggered');
}
emitter.on('event3', listener1);
emitter.on('event3', listener2);
emitter.emit('event3')
emitter.removeListener('event3',listener1)//两个参数
emitter.removeListener('event3',listener2)//两个参数
emitter.emit('event3')
定时器
setTimeOut以及setInterval, 当然还有setImmediate
/**
*
* setTimeout 延迟执行。只执行一次
* 删除:clearTimeOut(id)
*
*
*
* setInterval 定时隔多久执行一次
* 删除:clearInterval(id)
*
*/
setTimeout(function () {
console.log('hello,timeOut')
},2000)//2s
const timeOutId=setTimeout(function () {
console.log('id')
},1000)//1s
// clearTimeout(timeOutId) 清除timeOutId
/**
* 参数
*
* */
function timeOutFunc(param1,param2){
console.log(param1)
console.log(param2)
}
setTimeout(timeOutFunc,3000,'param1','param2')
setTimeout(function (param1, param2) {
console.log(param1+param2)
},4000,1,2)
/**
* 0延迟,尽快执行,但在当前函数执行之后
* */
setTimeout(function () {
console.log('after')
},0)
console.log('before-------before')
/**
*
* setInterval 定时执行
*
* */
let count=0;
const intervalId=setInterval(function () {
if(count++===2){
clearInterval(intervalId) //删除,但是删除之后依旧会执行一次后续代码
}
console.log('interval')
},2000)
/**
*
* 让一个函数同步执行,执行完这个函数之后间隔1s继续执行,然后间隔1s继续执行
*
* 为了防止函数执行交叉,使用如下方式
*
* 使用setInterval可能存在函数交叉,所以使用如下方式
* */
const myFunction = () => {
// do something
console.log('myFunction')
setTimeout(myFunction, 1000);
};
setTimeout(myFunction, 1000);