之前有篇Promise讲的更清楚 dd
异步不会阻塞后续代码执行
区别
1.返回值
同步:可以从返回值拿到返回的结果
异步:无法用返回值接受
同步API
异步函数返回值用回调函数的方式拿到
回调函数:自己定义函数让别人调用
方法内部达到条件后调用
将方法执行结果通过回调函数参数传出(因为无法return传递)
2.代码执行顺序
同步API从何是哪个到下依次执行,前面的代码会阻塞后面代码的执行
同步代码执行区
异步代码执行区
回调函数队列
遇到异步api放到异步队列
等同步代码都执行完后再执行异步队列
Node.js的异步api
fs.readFile('./.txt',(err,res) => {})
server.on('request',(req,res)=>{})
事件处理函数也是异步api
如果异步api后面的代码执行依赖当前api的执行结果,但后续代码在执行的时候异步api还没有返回结果
fs.readFile('./.txt', (err,res)=>{})
console.log('文件读取结果');
可以把输出放到api中
需求:依次读取a,b,c文件
如果全部嵌套 回调函数嵌套太多(回调地狱)
fs.readFile('./1.txt','utf8',(err ,res)=>{
fs.readFile('./2.txt','utf8',(err ,res)=>{
fs.readFile('./3.txt','utf8',(err ,res)=>{});
});
});
顺序没问题 但难以维护
回调地狱↑
Promise
解决Node.js异步编程中回调地狱的问题
只是改进语法的写法
上面的改写成:
// 三个异步api 三个 Promise对象
function p1(){
return new Promise((resolve,reject) =>{
fs.readFile('./1.txt','utf8',(err,res) =>{
resolve(res);
})
})
}
function p2(){
return new Promise((resolve,reject) =>{
fs.readFile('./2.txt','utf8',(err,res) =>{
resolve(res);
})
})
}
function p3(){
return new Promise((resolve,reject) =>{
fs.readFile('./3.txt','utf8',(err,res) =>{
resolve(res);
})
})
}
// 每个.then 返回一个Promise对象就可以链式调用 异步链
p1().then((r1)=>{
console.log(r1);
return p2();
})
.then((r2)=>{
console.log(r2);
return p3();
})
.then((r3)=>{
console.log(r3);
})
异步函数
比Promise更简洁
async function fn() {}
- 在普通函数定义前面加上async关键字 普通函数就变成了异步函数
- 异步函数默认的返回值是Promise对象(return的值包装在一个Promise对象中)
- 在异步函数内部使用throw关键字进行错误抛出(throw之后的代码不会执行 外部可以用catch或then的第二个参数接受)
await 关键字:
- 只能出现在异步函数中
- await promise 可以暂停异步函数的执行 等待Promise对象返回结果后再向下执行
async function p1(){
return 'p1';
}
async function p2(){
return 'p2';
}
async function p3(){
return 'p3';
}
async function run(){
await p1();
await p2();
await p3();
}
run()
省去了Promise的创建 比上面Promise更简洁
应用
改写 依次读取三个对象
node.js 提供了一个方法 可以包装异步api 让api返回Promise对象 支持异步语法
util模块的 promisify方法
改装现有异步api, 让api返回Promise对象,以支持异步语法
const fs = require('fs');
const promisify = require('util').promisify; // 将方法储存在变量中
const readFile = promisify(fs.readFile); // 用promisify处理后 readFile方法返回Promise对象
async function run() {
let r1 = await readFile('./1.txt','utf8'); //不需要回调函数了
let r2 = await readFile('./1.txt','utf8');
let r3 = await readFile('./1.txt','utf8');
console.log(r1);
console.log(r2);
console.log(r3);
}
//相较于 前两种写法就更简洁了