五、异步API
导学:
通过返回值拿结果
path.join()
通过函数拿结果,fs.redFile~
在node中有两种api
同步的api还有异步的api
-
同步
所谓的同步就是一步一步的走 -
异步
当前的api不会堵塞后续的代码的执行 -
对比
-
第一个区别
二者最大的区别:同步的api可以从返回值拿结果,异步的就不行
你只需要知道,异步的api是通过回调函数拿结果 -
第二个区别
同步的api会堵塞,我们的异步api不会堵塞
注意:同步还有异步还有任务队列都是非常基础的东西,这里做过多的解释,有需要可以去百度查询对应的东西
(一)、Node中的异步api
不能通过返回值拿结果
这里举例说明
读取文件的操作是异步的
fs.readFile('./demo.txt',( err,result )=>{
})
事件监听也是异步的
let server = http.creareServe();
server.on('request', ( req.res )=>{})
// ....当然了我们还有很多的异步api,这里不一一列举
- 提出问题
如果异步后面的api依赖前面的api的执行结果怎么办??
解决1:后续的操作写在回调里面,不推荐,因为太复杂,回调层次太多,不好维护,会变 ‘回调地狱’,比如下面的代码
//这里的代码,旨在依次的读取 A B C三个文件
const fs = require('fs');
//通过回调函数依次的执行,这个是不可维护的代码,回调回调,再回调=哭了,回调地狱
fs.readFile('./1.txt', 'utf8', (err, result1) => {
console.log(result1)
fs.readFile('./2.txt', 'utf8', (err, result2) => {
console.log(result2)
fs.readFile('./3.txt', 'utf8', (err, result3) => {
console.log(result3)
})
})
});
解决2:Promise对象 ,解决异步回调的问题,以下来看看使用的步骤
简单的使用
const fs = require('fs');
// 说明“1. 异步api的结果与异步相分离
// 说明“2. resolve reject都是两个函数,第一一个把正确的结果丢到外面出去。第二个就是把失败的结果丢到外面去
//1.实例化对象
let promise = new Promise(
//2.实例化的时候把匿名函数弄进去
(resolve, reject) => {
fs.readFile('./100.txt', 'utf8', (err, result) => {
//业务逻辑,如果那么
if (err != null) {
reject(err);
} else {
resolve(result);
}
});
}
);
// 说明“3. 通过the方法,把 resolve reject接过来
//3.在外面接受结果
promise.then((result) => {
console.log(result);
})
.catch((err) => {
console.log(err);
})
看看我们怎么解决之前的回调地狱问题
//1.实例化对象,注意我们要保证依次的执行就需要放在函数里面
// let p1 = new Promise((resolve, reject) => {
// fs.readFile('./1.txt', (erro, result) => {
// // 读取之后调用这个参数函数,把成功的结果丢到外面去
// resolve(result)
// })
// })
// let p3 = new Promise((resolve, reject) => {
// fs.readFile('./2.txt', (erro, result) => {
// resolve(result)
// })
// })
// let p1 = 3 = new Promise((resolve, reject) => {
// fs.readFile('./3.txt', (erro, result) => {
// resolve(result)
// })
// })
// function p1() {
// let p1 = new Promise((resolve, reject) => {
// fs.readFile('./1.txt', (erro, result) => {
// // 读取之后调用这个参数函数,把成功的结果丢到外面去
// resolve(result)
// })
// })
// }
// function p2() {
// let p2 = new Promise((resolve, reject) => {
// fs.readFile('./2.txt', (erro, result) => {
// // 读取之后调用这个参数函数,把成功的结果丢到外面去
// resolve(result)
// })
// })
// }
// function p3() {
// let p3 = new Promise((resolve, reject) => {
// fs.readFile('./3.txt', (erro, result) => {
// // 读取之后调用这个参数函数,把成功的结果丢到外面去
// resolve(result)
// })
// })
// } 默认函数返回undefined.then???
// 细节,只有我们的promise对象才能.then啥啥啥的。我的意思是只有 promise对象才行执行它应该有的操作,所以你需要在函数里面把promise返回出来
function p1() {
// 为什么要rreturn是因为,我们在后学的p1().then()中要有返回值
return new Promise((resolve, reject) => {
fs.readFile('./1.txt', 'utf8', (err, result) => {
resolve(result)
})
});
}
function p2() {
return new Promise((resolve, reject) => {
fs.readFile('./2.txt', 'utf8', (err, result) => {
resolve(result)
})
});
}
function p3() {
return new Promise((resolve, reject) => {
fs.readFile('./3.txt', 'utf8', (err, result) => {
resolve(result)
})
});
}
// 2. 在外面接收,处理
// 这里的r1 r1是接受的一个形参数,相当与r1 = 异步的结果,这样子的代码是不纯在嵌套的
p1().then((r1) => {
console.log(r1);
//注意:我们是在p1再把retun出去,下一个the就是这个retrub的结果,而这个retun的结果就保存到r2中了
return p2(); //这是语法,不讲了,获取的就是一个promise对象
})
//链式编程,直接点击then
.then((r2) => {
console.log(r2);
return p3();
})
.then((r3) => {
console.log(r3)
})
不过,现在又有问题了,我还是觉得这个代码太复杂的,我们有没有什么方式,把异步的写成同步的样子。我的意思是说。我想让我的异步代码看起来像同步代码一样,因为我看起来舒服。看起来简单明了。
解决:异步函数,async,在普通的函数前加一个这个关键字 ,我们的函数就变成异步的了
语法代码演示:
// 1.在普通函数定义的前面加上async关键字 普通函数就变成了异步函数
// 2.异步函数默认的返回值是promise对象不是undecided!!!
// 3.在异步函数内部使用throw关键字进行错误的抛出
//
// await关键字
// 1.它只能出现在异步函数中
// 2.await promise 它可以暂停异步函数的执行 等待promise对象返回结果后再向下执行函数
// async function fn () {
// throw '发生了一些错误';
// return 123;
// }
// // console.log(fn ())
// fn ().then(function (data) {
// //then是为了回去异步函数的结果
// console.log(data);
// }).catch(function (err){
// console.log(err);
// })
async function p1() {
return 'p1';
}
async function p2() {
return 'p2';
}
async function p3() {
return 'p3';
}
//通过异步函数再去一个一个的跑出来!
async function run() {
//这里是一个 await
let r1 = await p1() //只要没有拿到P1回送的promise对象,我们就不向下执行,这个就是await的作用
let r2 = await p2()
let r3 = await p3()
console.log(r1)
console.log(r2)
console.log(r3)
}
run();
总结:
- async关键词
- 普通函数定义前加async关键字 普通函数变成异步函数
- 异步函数默认返回promise对象
- 在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了resolve方法
- 在异步函数内部使用throw关键字抛出程序异常
- 调用异步函数再链式调用then方法获取异步函数执行结果
- 调用异步函数再链式调用catch方法获取异步函数执行的错误信息
- await关键字
- await关键字只能出现在异步函数中
- await promise await后面只能写promise对象 写其他类型的API是不不可以的
- await关键字可是暂停异步函数向下执行 直到promise返回结果
解决之前的读取文件的
const fs = require('fs');
// 1. 改造现有异步函数api 让其返回promise对象 从而支持异步函数语法,异步语法去包装原来的异步api返回的结果
const promisify = require('util').promisify; //用一个变量去获得这个 promisify 方法
// 2. 调用promisify方法改造现有异步API 让其返回promise对象
//我们看一下到底拿到了什么鬼东西
console.log(promisify);
//{ [Function: promisify] custom: Symbol(util.promisify.custom) }
const readFile = promisify(fs.readFile); //这个返回值是一个promise对象
//我们看一下到底拿到了什么鬼东西
// [Function: readFile],这个就是我们的代码
console.log(readFile);
//3.于是乎我们的阿的
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();
(二)、全局对象global
不知道你有没有注意到,在学习web API的时候我们说过 我们的 console.log()是window下面,但是我们的node运行中也可以使用,那么有问题来了,我们的这里的console.log()是谁的?答案是:global的
(三)、网站服务器
一个完整的网站就是一个,包含两个部分:浏览器还有一个与之对应的服务器
接下里,我们来学习轻量级且强大的MongoDB数据库软件,还有我们的这个Node服务器开发框架Express