Node.js的异步编程
1、同步API
指只有当前在API执行完成后,才执行下一个API,代码执行方式是按代码的先后顺序从上到下执行
2、异步API
当前执行不会阻塞后续代码的执行,代码执行方式是当执行到耗时代码时不会等待耗时操作完成再去执行下一条代码,执行结果由回调函数执行
演示:
console.log('1')
setTimeout(function(){
console.log('2')
},2000)
console.log('3')
3、回调地狱
回调地狱:如果异步API后面代码的执行依赖当前异步API的执行结果,这就需要把代码写在回调函数中。一旦回调函数的嵌套层次过多,就会导致代码不易维护,我们将这种代码形象地称为回调地狱。
演示:
提前创建三个文本文件:
const fs = require('fs')
fs.readFile('./txt/a.txt','utf8',(err,result1)=>{
console.log(result1)
fs.readFile('./txt/b.txt','utf8',(err,result2)=>{
console.log(result2)
fs.readFile('./txt/c.txt','utf8',(err,result3)=>{
console.log(result3)
})
})
})
4、回调地狱的解决方法:利用Promise解决回调地狱问题
4.1、promise本身是一个构造函数,如果需要用promise解决回调地狱的问题,需要new关键字创建Promise构造函数的实例对象。
// 定义Promise
let promise = new Promise((resolve, reject) => { });
resolve:是一个函数
reject:是一个函数
promise.then(result => console.log(result))
.catch(error => console.log(error));
演示:
const fs = require('fs')
function p1(){
let promise = new Promise((resolve,reject)=>{ //创建promise对象
fs.readFile('./txt/a.txt','utf8',(err,result)=>{
if(err!=null){
reject(err)
}
resolve(result)
})
})
return promise
}
function p2(){
let promise = new Promise((resolve,reject)=>{ //创建promise对象
fs.readFile('./txt/b.txt','utf8',(err,result)=>{
if(err!=null){
reject(err)
}
resolve(result)
})
})
return promise
}
function p3() {
let promise = new Promise((resolve,reject)=>{ //创建promise对象
fs.readFile('./txt/c.txt','utf8',(err,result)=>{
if(err!=null){
reject(err)
}
resolve(result)
})
})
return promise
}
p1().then((r1)=>{
console.log(r1)
return p2()
})
.then((r2)=>{
console.log(r2)
return p3()
})
.then((r3)=>{
console.log(r3)
})
.catch((err)=>{
console.log(err)
})
4.2、什么时候用Promise?
在程序中出现回调函数嵌套时,建议使用Promise
5、异步函数
异步函数:异步函数实际上是在Promise对象的基础上进行了封装,它把一些看起来比较繁琐的代码封装起来, 然后开放一些关键字供开发者来使用.异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。
5.1、async关键字:在函数定义放在函数的前面,标识该函数是一个异步调用函数,其返回值是一个Promise对象
5.2、await关键字:
(1)、可以暂停异步函数的执行,等待Promise对象返回结果再向下执行函数
(2)、只能出现在异步函数中,await后面只能写Promise对象,不能写其他类型API使异步函数调用后,得到一个同步的效果。
演示1:
async function fn(){
//throw '发生错误'
return 123
}
fn().then((data)=>{ //调用fn函数,将该函数的返回值传递给then的回调函数
console.log(data)
}).catch((err)=>{ //catch的回调函数接受的是fn函数当中throw抛出的异常信息
console.log(err)
})
演示2:
将上面利用Promise解决回调地狱问题的代码进行改造:
const fs = require('fs')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)
async function p1(){
let r1 = await readFile('./txt/a.txt','utf8')
console.log(r1)
let r2 = await readFile('./txt/b.txt','utf8')
console.log(r2)
let r3 = await readFile('./txt/c.txt','utf8')
console.log(r3)
}
p1()
可以看到异步函数相比较于Promise对象使用起来马,代码更为简洁。