一、Promise
1.1回调地狱。
多层函数的相互嵌套,就形成了回调地狱。
setTimeout(()=>{
console.log('延时1秒');
setTimeout(()=>{
console.log('延时2秒');
setTimeout(()=>{
console.log('延时3秒')
},3000)
},2000)
},1000)
回调地狱的缺点:
①代码耦合性太强,牵一发而动全身,难以维护。
②大量冗余的代码相互嵌套,代码的可读性差。
1.2如何解决回调地狱。
使用promise 构造函数 解决回调地狱的问题,ES6中新增了Promise的概念。
1.3Promise的基本概念
1.3.1 Promise是一个构造函数
我们可以创建Promise的实例 cont p = new Promise()
new 出来的Promise 的实例对象,代表一个异步操作。
1.3.2Promise.prototype上包含一个.then()方法
每次new Promise()构造函数得到的实例对象
都可以通过原型链的方式访问到.then()方法 例如p.then()
1.3.3 .then()方法 用来预先指定成功和失败的回调函数。
p.then(成功的回调函数,失败的回调函数)
p.then(result =>{},error=>{})
调用.then()方法时,成功的回调函数是必选的,失败的回调函数是可选的
2.基于回调函数按顺序读取文件的内容
fs.readFild('./files/1.txt', 'utf8', (err1, r1) => {
if (err1) return console.log(err1.message);
fs.readFild('./files/2.txt', 'utf8', (err2, r3) => {
if (err2) return console.log(err2.message);
fs.readFild('./files/3.txt', 'utf8', (err3, r3) => {
if (err3) return console.log(err3.message);
});
});
});
3基于 then-fs 读取文件内容
由于 node.js官方提供的fs模块仅支持以回调函数的方式读取文件,不支持Promise的调用方式,因此,需要先运行如下的命令,安装 then-fs 第三方包
npm install then-fs
3.1 then-fs 的基本使用。
调用then-fs提供的readFile()方法,可以异步的读取文件的内容,它的返回值是Promise的实例对象,因此可以调用.then()方法 为每个Promise异步操作指定成功和失败之后的回调函数。
import thenFs from 'then-fs';
thenFs.readFile('./files/1.txt', 'utf8').then((r1) => {
console.log(r1);
});
thenFs.readFile('./files/2.txt', 'utf8').then((r2) => {
console.log(r2);
});
thenFs.readFile('./files/3.txt', 'utf8').then((r3) => {
console.log(r3);
});
3.2 .then()方法的特性
如果上一个 .then()方法中返回了一个新的Promise实例对象,则可以通过下一个.then() 继续进行处理。通过 .then()方法的链式调用,就解决了回调地狱的问题。
3.3 基于Promise按顺序读取文件的内容
Promise支持链式调用, 从而来解决回调地狱的问题
import thenFs from 'then-fs';
thenFs
.readFile('./files/1.txt', 'utf8') //1、返回值是Promise的实例对象
.catch((err) => {
console.log(err.message);
})
.then((r1) => {
//2、通过 .then为第一个Promise实例指定成功之后的回调函数
console.log(r1);
return thenFs.readFile('./files/2.txt', 'utf8'); //3、在第一个.then中返回的新的Promise实例对象
})
.then((r2) => {
//4、继续调用.then为上一个.then的返回值指定成功之后的回调函数
console.log(r2);
return thenFs.readFile('./files/3.txt', 'utf8'); //5、在第二个.then中返回的新的Promise实例对象
})
.then((r3) => {
//6、继续调用.then为上一个.then的返回值指定成功之后的回调函数
console.log(r3);
});
二、async/await
1.什么是async/await?
async/await是ES8 中新引入的语法, 用来简化Promise异步操作,在async/await 出现之前,开发者只能通过链式.then()方式处理 Promise异步操作
.then 链式调用的优点:解决了回调地狱的问题
.then 链式调用的缺点:代码冗余 阅读性差 不易理解
2.async / await 的基本使用
import thenFs from 'then-fs';
async function getAllFile() {
const r1 = await thenFs.readFile('./files/1.txt', 'utf8');
console.log(r1);
const r2 = await thenFs.readFile('./files/2.txt', 'utf8');
console.log(r2);
const r3 = await thenFs.readFile('./files/3.txt', 'utf8');
console.log(r3);
}
getAllFile();
3.async/wait 的使用注意事项
①如果在function中使用了 await 则function 必须被async修饰
②在async 方法中,第一个await之前的代码会被同步执行,await之后的代码会被异步执行。
import thenFs from 'then-fs';
console.log('A');
async function getAllFile() {
console.log('B');
const r1 = await thenFs.readFile('./files/1.txt', 'utf8');
console.log(r1);
const r2 = await thenFs.readFile('./files/2.txt', 'utf8');
console.log(r2);
const r3 = await thenFs.readFile('./files/3.txt', 'utf8');
console.log(r3);
console.log('D');
}
getAllFile();
console.log('C');
结果
A
B
C
111 222 333
D
三、EventLoop(事件循环)
1.java 是一门单线程的语言。
JavaScript 是一门单线程执行的编程语言。也就是说,同一时间只能做一件事情。
单线程执行任务队列的问题:
如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题。
2.同步任务和异步任务:
为了防止某个耗时任务导致程序假死的问题, JavaScript 把待执行的任务分为了两类:
- 同步任务 (synchronous)
又叫做非耗时任务,指的是在主线程上排队执行的那些任务
只有前一个任务执行完毕,才能执行后一个任务
- 异步任务(asynchronous)
又叫做耗时任务,异步任务由 JavaScript 委托给宿主环境进行执行
当异步任务执行完成后,会通知 JavaScript 主线程执行异步任务的回调函数
例子:ajax
3.同步任务和异步任务的执行过程。
4.EventLoop的基本概念、经典面试题
JavaScript 主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这个过程是循环不断的, 所以整个的这种运行机 制又称为 EventLoop (事件循环) 。
console.log('a');
setTimeout(function () {
console.log('b');
}, 2000);
setTimeout(function () {
console.log('c');
}, 0);
console.log('d');
正确的输出结果: ADCB。其中:
1. A 和 D 属于同步任务。会根据代码的先后顺序依次被执行
2. C 和 B 属于异步任务。它们的回调函数会被加入到任务队列中,等待主线程空闲时再执行
四、宏任务/微任务
1.什么是宏任务和微任务?
JavaScript把异步任务又做了进一步的划分,划分成了宏任务和异步任务。
①宏任务(macrotask)
异步Ajax请求
setTimemout setInterval
文件操作
其他宏任务
②微任务(microtask)
Promise.then .catch和.finally
process.nextTick
其他微任务
2.宏任务和微任务的执行顺序。
每一个宏任务执行完成之后,都会检查是否存在待执行的微任务,如果有则执行完所有微任务后,再继续执行下一个宏任务。
3.去银行办业务的场景
案例:
答案:2431
答案 156234789