01-Promise语法
1.1-Promise介绍
- ES6教程传送门:http://es6.ruanyifeng.com/#docs/promise
- 1.Promise是什么?
- Promise
是
一个构造函数
, 用于创建Promise对象- Promise对象:可以理解为一个处理异步操作的容器
- Promise
- 2.Promise作用:解决回调地狱
- 回调地狱:异步回调 层层嵌套
/*
为什么要学习Promise?
1.Promise作用 : 解决回调地狱问题
回调地狱 : 异步回调 层层嵌套
*/
//需求: 依次读取 文件 a.txt , b.txt , c.txt 这三个文件内容
const fs = require('fs');
//(1)能直接按照顺序写吗? : 不能,因为异步操作 是无序的
fs.readFile("./data/a.txt", 'utf-8', (err, data) => {
if(err){
console.log(err);
}else{
console.log(data);
};
});
fs.readFile("./data/b.txt", 'utf-8', (err, data) => {
if(err){
console.log(err);
}else{
console.log(data);
};
});
fs.readFile("./data/c.txt", 'utf-8', (err, data) => {
if(err){
console.log(err);
}else{
console.log(data);
};
});
//(2)解决方案 : 在回调函数中嵌套执行
//弊端 : 形成回调地狱(异步回调 层层嵌套,非常麻烦且不便于维护)
//读取文件A
fs.readFile("./data/a.txt", 'utf-8', (err, data) => {
if(err){
console.log(err);
}else{
console.log(data);
//A读取成功之后开始读取B
fs.readFile("./data/b.txt", 'utf-8', (err, data) => {
if(err){
console.log(err);
}else{
console.log(data);
//B读取成功之后开始读取C
fs.readFile("./data/c.txt", 'utf-8', (err, data) => {
if(err){
console.log(err);
}else{
console.log(data);
}
});
}
});
}
});
1.2-Promise基本使用
- 1.实例化Promise对象
- 作用: 将异步操作代码 放入 Promise中
- resolve:异步操作 成功状态
- reject : 异步操作 失败状态
new Promise((resolve,reject)=>{ 你的异步操作 })
- 作用: 将异步操作代码 放入 Promise中
- 2.调用
then()
方法处理异步操作结果promise对象.then((data)=>{ 处理成功数据 },(err)=>{ 处理失败信息 });
/*
1.Promise是一个构造函数 , 返回一个Promise对象
2.使用流程
(1)实例化Promise对象 : 将异步操作放入Promise中
(2)调用then() 方法: 处理异步操作结果
*/
const fs = require('fs');
/** 1. 实例化Promise
* @description:实例化Promise
* @param {Function} (resolve:成功处理函数,reject:失败处理函数)=>{ 异步操作代码 }
* @return: Promise对象
*/
const p1 = new Promise((resolve,reject)=>{
//读文件
fs.readFile('./data/a.txt','utf8',(err,data)=>{
if(err == null){
/*
(1)异步操作成功,则执行 resolvce()
(2)resolve会把把promise对象的状态从 pending进行中 改为 fulfilled成功
(3)该方法本质是调用 then() 中的第一个方法
*/
resolve(data);
}else {
/*
(1)异步操作失败,则执行 reject()
(2)reject会把把promise对象的状态从 pending进行中 改为 rejected失败
(3)该方法本质是调用 then() 中的第二个方法
*/
reject(err);
}
});
});
/* 2. p1.then() : 处理异步操作结果 */
p1.then((data)=>{
//成功了,打印文件数据
console.log(data);
},(err)=>{
//失败了,打印错误信息
console.log(err);
});
1.3-Promise特点介绍(原理篇)
promise本身并没有改变异步的顺序,而是通过
操作
异步结果的顺序从而'间接'
控制异步的顺序
promise本质 不是控制
异步代码的执行顺序
(无法控制) , 而是控制
异步代码结果处理的顺序
**promise本身只是一个容器,真正异步的是它的两个回调resolve()和reject() **
/**
* 1.promise对象有三个状态.
a. pending(进行中)
b. fulfilled(已成功)
c. rejected(已失败)
* 2.Promise对象的状态改变, 只有两种可能:
a. 从pending变为fulfilled
* 此时应该执行 resolve();
b. 从pending变为rejected。
* 此时应该执行 reject();
* 3.promise在创建对象的时候,里面的代码会立即执行.
a. promise创建时,里面的代码还是异步无序操作
b. promise的原理是,利用then方法将异步操作的结果 按照顺序执行
*** 总结: 不要在创建promise的时候去处理异步操作结果,而应该通过 then() 方法来处理 ***
* 4.promise解决回调地狱原理 : 在then方法中返回一个promise对象
*** 在上一个promise的then方法中,返回下一个promise ***
* 5.坤哥结语 : promise本质 不是控制异步代码的执行顺序(无法控制) , 而是控制异步代码结果处理的顺序
*/
const fs = require('fs');
//(1) 创建三个异步操作 promise
//读取文件A
const p1 = new Promise((resolve,reject)=>{
//读文件
fs.readFile('./data/a.txt','utf8',(err,data)=>{
if(err == null){
//成功
resolve(data);
}else {
//失败
reject(err);
}
});
});
//读取文件B
const p2 = new Promise((resolve,reject)=>{
//读文件
fs.readFile('./data/b.txt','utf8',(err,data)=>{
if(err == null){
//成功
resolve(data);
}else {
//失败
reject(err);
}
});
});
//读取文件C
const p3 = new Promise((resolve,reject)=>{
//读文件
fs.readFile('./data/c.txt','utf8',(err,data)=>{
if(err == null){
//成功
resolve(data);
}else {
//失败
reject(err);
}
});
});
// (2)按照顺序处理异步操作结果
p1.then((data)=>{//第一个异步结果
console.log(data);
return p2;//返回下一个promise
}).then((data)=>{ // 第二个异步结果 (由于p1.then方法返回的是p2,而p2也有自己的then,所以可以继续调用p2的then方法)
console.log(data);
return p3;//返回下一个promise
}).then((data)=>{ // 第三个异步结果
console.log(data);
});
1.4-Promise解决回调地狱封装
const fs = require('fs');
//1.封装一个函数 : 根据文件名生成 文件读取的promise
function getPromise(fileName) {
let p = new Promise((resolve, reject) => {
//读文件
fs.readFile(`./data/${fileName}.txt`,'utf-8', (err, data) => {
if (err == null) {
//成功
resolve(data);
} else {
//失败
reject(err);
}
});
});
return p;
};
//2.解决需求: 要先读a, 读完a后读b, 读完b后读c.
//开始读取a
getPromise('a').then((data)=>{
console.log(data);
//继续读取b
return getPromise('b');
}).then((data)=>{
console.log(data);
//继续读取c
return getPromise('c');
}).then((data)=>{
console.log(data);
});//异步回调队列结束
1.5-Promise对象的catch方法
- catch:用于捕获异步操作的错误信息
const fs = require("fs");
/*
promise实例对象的catch方法 : 用于捕获异步操作的错误信息
*/
//1.封装一个函数 : 根据文件名生成 文件读取的promise
function getPromise(fileName) {
let p = new Promise((resolve, reject) => {
//读文件
fs.readFile(`./data/${fileName}.txt`, 'utf-8', (err, data) => {
if (err == null) {
//成功
resolve(data);
} else {
//失败
reject(err);
}
});
});
return p;
};
//2.解决需求: 要先读a, 读完a后读b, 读完b后读c.
//开始读取a
getPromise('a').then((data)=>{
console.log(data);
//继续读取b
return getPromise('b');
}).then((data)=>{
console.log(data);
//继续读取c
return getPromise('c');
}).then((data)=>{
console.log(data);
}).catch((err)=>{
//以上三个异步操作,只要有任何一个出错,都会执行err
console.log(err);
});
1.6-Promise对象的all方法
- all
- 将多个Promise合并成一个Promise
- 所有异步全部执行完毕才会执行then方法
const fs = require("fs");
/*
1. promise实例对象的all方法 : 将多个Promise合并成一个Promise
* 所有异步全部执行完毕才会执行then方法
2. 解决需求: a , b , c 同时执行完毕
*/
//1.封装一个函数 : 根据文件名生成 文件读取的promise
function getPromise(fileName) {
let p = new Promise((resolve, reject) => {
//读文件
fs.readFile(`./data/${fileName}.txt`, 'utf-8', (err, data) => {
if (err == null) {
//成功
resolve(data);
} else {
//失败
reject(err);
}
});
});
return p;
};
//2.解决需求: a , b , c 同时执行完毕
let pa = getPromise('a');
let pb = getPromise('b');
let pc = getPromise('c');
//将三个异步操作合并成一个Promise
let pAll = Promise.all([pa,pb,pc]);
//开始读取a
// 三个promise都成功后,才去执行pAll.then的第一个方法.
// 只要有一个失败了,就去执行catch里面的函数.
pAll.then((data)=>{
console.log(data);//data是一个数组,存储每一个promise的成功结果
}).catch((err)=>{
console.log(err);
});
1.7-Promise对象的race方法
- race
- 将多个Promise合并成一个Promise
- 任何一个异步 执行完毕就会执行then方法
const fs = require("fs");
/*
1. promise实例对象的race方法 : 将多个Promise合并成一个Promise
* 任何一个异步 执行完毕就会执行then方法
2. 解决需求: a , b , c 任意一个成功
*/
//1.封装一个函数 : 根据文件名生成 文件读取的promise
function getPromise(fileName) {
let p = new Promise((resolve, reject) => {
//读文件
fs.readFile(`./data/${fileName}.txt`, 'utf-8', (err, data) => {
if (err == null) {
//成功
resolve(data);
} else {
//失败
reject(err);
}
});
});
return p;
};
//2.解决需求: a , b , c 同时执行完毕
let pa = getPromise('a');
let pb = getPromise('b');
let pc = getPromise('c');
//将三个异步操作合并成一个Promise
let pAll = Promise.race([pa,pb,pc]);
//开始读取a
// 三个promise任何一个成功,就去执行pAll.then的第一个方法. (一旦成功一个,其他不再执行)
pAll.then((data)=>{
console.log(data);//data是第一个执行成功的primise异步结果
}).catch((err)=>{
console.log(err);
});
1.8-[拓展了解]axios底层基于promise
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
/*
1.axios() : 返回值是promise实例对象
2.axios() : 底层是基于promise技术封装xhr
*/
//手写axios底层原理
function axios(obj){
return new Promise((resolve,reject)=>{
//(1).实例化ajax对象
let xhr = new XMLHttpRequest()
//(2).设置请求方法和地址ii
//get请求的数据直接添加在url的后面 格式是 url?key=value
xhr.open('get', obj.url )
//(3).发送请求
xhr.send()
//(4).注册回调函数
xhr.onload = function() {
resolve( JSON.parse(xhr.responseText) )
}
})
}
// const p1 = axios()// axios()方法本质是创建promise对象并返回
// console.log( p1 )//promise实例对象
Promise.all( [axios({url:'https://autumnfish.cn/api/joke/list?num=10'}) , axios({url:'https://autumnfish.cn/fruitApi/fruits'})] ).then(res=>{
console.log(res)
})
</script>
</body>
</html>
02-ES6异步函数async与await
异步函数async相当于是promise语法的 “高级写法”
ES2017中引入的更为高级的异步处理机制,
async
函数,可以让异步的处理变的更加便捷
一句话概括: async函数相当于是promise异步函数的另一种
高级写法
- promise虽然解决了异步回调地狱(回调函数层次嵌套)的问题,但是写起来的时候仍然需要嵌套(链式语法嵌套,需要在上一个promise对象的then方法中返回下一个promise)
1.传统的promise异步函数(模拟依次异步读取文件a,b,c)
const fs = require("fs");
/*
promise实例对象的catch方法 : 用于捕获异步操作的错误信息
*/
//1.封装一个函数 : 根据文件名生成 文件读取的promise
function getPromise(fileName) {
let p = new Promise((resolve, reject) => {
//读文件
fs.readFile(`./data/${fileName}.txt`, 'utf-8', (err, data) => {
if (err == null) {
//成功
resolve(data);
} else {
//失败
reject(err);
}
});
});
return p;
};
//2.解决需求: 要先读a, 读完a后读b, 读完b后读c.
//开始读取a
getPromise('a').then((data)=>{
console.log(data);
//继续读取b
return getPromise('b');
}).then((data)=>{
console.log(data);
//继续读取c
return getPromise('c');
}).then((data)=>{
console.log(data);
}).catch((err)=>{
//以上三个异步操作,只要有任何一个出错,都会执行err
console.log(err);
});
2.使用async异步函数
async语法如下
- (1)函数前面使用
async
修饰 - (2) 函数内部,promise操作使用
await
修饰await
后面是promise对象
, 左侧的返回值就是这个promise对象的then方法中的结果await
必须要写在async
修饰的函数中,不能单独使用,否则程序会报错
- (3)
async
函数内部的异常需要通过try
,catch
来捕获
- (1)函数前面使用
const fs = require("fs");
/*
promise实例对象的catch方法 : 用于捕获异步操作的错误信息
*/
//1.封装一个函数 : 根据文件名生成 文件读取的promise
function getPromise(fileName) {
let p = new Promise((resolve, reject) => {
//读文件
fs.readFile(`./data/${fileName}.txt`, 'utf-8', (err, data) => {
if (err == null) {
//成功
resolve(data);
} else {
//失败
reject(err);
}
});
});
return p;
};
//2.解决需求: 要先读a, 读完a后读b, 读完b后读c.
// async和await异步函数 : 这两个关键字只能用于函数, 所以用的时候一定要放在函数里面用
/*
async关键字: 修饰函数。 表示这个函数内部有异步操作。
await关键字: 等待异步执行完毕。
(1)await只能用于被async修饰的函数中。
只有当await后面的异步操作执行完毕后,才会继续执行后面代码
(2)await 后面 只能是promise对象
*/
const readFile = async () => {
let data1 = await getPromise('a')
console.log(data1)
let data2 = await getPromise('b')
console.log(data2)
//async异步函数的错误信息要用try-catch来捕捉
try {
let data3 = await getPromise('c')
console.log(data3)
} catch (err) {
console.log(err)
}
}
readFile()
03-异步函数实际应用场景
1.水果列表
- 请求地址:https://autumnfish.cn/fruitApi/fruits
- 请求方法:get
- 请求参数:
- 响应内容:json数据
2.水果详情
- 请求地址: https://autumnfish.cn/fruitApi/fruit/:id
- 请求方法: get
- 请求参数:id,在 url 中直接传递
- 响应内容:json
- icon:base64 的图片,直接设置给
src
即可使用
- icon:base64 的图片,直接设置给
需求: (1)先请求水果列表 (2)默认请求第一个水果详细信息
思考: 这两个ajax有没有顺序要求?
是不是一定要先发送第一个ajax,只有第一个ajax响应了。才可以拿到水果的id来发第二个ajax?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button class="btn1">按钮1:点我发送ajax</button>
<br>
<button class="btn2">按钮2:点我发送ajax</button>
<!-- 导入axios -->
<script src="./axios.js"></script>
<script>
/* 需求:(1)先请求水果列表 (2)然后默认请求第一个水果详情 */
//没有使用异步函数
document.querySelector('.btn1').addEventListener('click',function(){
//(1)请求水果列表
axios({
url:'https://autumnfish.cn/fruitApi/fruits',
method:'get',
}).then(res=>{
//成功回调
console.log(res)
//(2)请求水果详情
axios({
url:`https://autumnfish.cn/fruitApi/fruit/${res.data.data[0].id}`,
method:'get',
}).then(res=>{
//成功回调
console.log(res)
})
})
})
//使用异步函数
document.querySelector('.btn2').addEventListener('click',async function(){
//(1)请求水果列表
let res1 = await axios({
url:'https://autumnfish.cn/fruitApi/fruits',
method:'get'
})
console.log(res1)
//(2)请求水果详情
let res2 = await axios({
url:`https://autumnfish.cn/fruitApi/fruit/${res1.data.data[0].id}`,
method:'get'
})
console.log(res2)
})
</script>
</body>
</html>
04-JS执行原理(Event Loop事件循环、微任务、宏任务)
1.1-事件循环Event Loop概念介绍
事件循环Event Loop又叫事件队列,两者是一个概念
- 1.什么是事件循环 : 浏览器解析执行js代码的一种运行机制(执行规则)
- 事件循环指的是js代码所在运行环境(浏览器、nodejs)编译器的一种解析执行规则。
在js中讨论事件循环是没有意义的,
: 事件循环不属于js代码本身的范畴,而是属于js编译器的范畴- 说人话: js代码可以理解为是一个人在公司中具体做的事情, 而 事件循环 相当于是公司的一种规章制度。 两者不是一个层面的概念。
- 事件循环指的是js代码所在运行环境(浏览器、nodejs)编译器的一种解析执行规则。
1.2-微任务、宏任务概念介绍
1.微任务与宏任务就属于js代码的范畴
2.js代码主要分为两大类: 同步代码、异步代码
3.异步代码又分为:微任务与宏任务
同步任务
:同步任务不需要进行等待,必须立即看到执行结果,比如console异步任务
:异步任务需要等待一定的时候才能看到结果,比如setTimeout、网络请求异步任务,又可以细分为宏任务和微任务。下面列举目前学过的宏任务和微任务。
任务(代码) | 宏/微 任务 | 环境 |
---|---|---|
1.3-事件循环Event Loop执行机制
- 1.进入到script标签,就进入到了第一次事件循环.
- 2.遇到同步代码,立即执行
- 3.遇到宏任务,放入到宏任务队列里.
- 4.遇到微任务,放入到微任务队列里.
- 5.执行完所有同步代码
- 6.执行微任务代码
- 7.微任务代码执行完毕,本次队列清空
- 寻找下一个宏任务,重复步骤1
- 以此反复直到清空所以宏任务,这种不断重复的执行机制,就叫做事件循环
05-事件循环Event Loop面试题大礼包
(1)promise嵌套promise : 先走嵌套的then,后走外层then
- 原理: promise本身是一个同步的代码(只是容器),
只有容器等同步走完了,才会走微任务then()
- 原理: promise本身是一个同步的代码(只是容器),
(2)await : 下面的才是微任务, 右边的代码还是立即执行
await微任务可以转换成等价的promise微任务分析
await 函数名() 代码块 //等价于下面代码 函数名().then(()=>{ 代码块 })
await fn() console.log(1111) //等价于 fn().then(()=>{ console.log(1111) })
(3)script标签本身是一个
宏任务
- 当页面出现多个script标签的时候,浏览器会把script标签作为宏任务来
解析
圈重点
: 解析不代表执行
- 当页面出现多个script标签的时候,浏览器会把script标签作为宏任务来
1.1-第一题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
console.log(1)
setTimeout(function () {
console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
resolve(1000)
})
p.then(data => {
console.log(data)
})
console.log(3)
</script>
</body>
</html>
1.2-第二题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
console.log(1)
setTimeout(function () {
console.log(2)
new Promise(function (resolve) {
console.log(3)
resolve()
}).then(function () {
console.log(4)
})
})
new Promise(function (resolve) {
console.log(5)
resolve()
}).then(function () {
console.log(6)
})
setTimeout(function () {
console.log(7)
new Promise(function (resolve) {
console.log(8)
resolve()
}).then(function () {
console.log(9)
})
})
console.log(10)
</script>
</body>
</html>
1.3-第三题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
console.log(1)
setTimeout(function () {
console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
console.log(3)
resolve(1000) // 标记为成功
console.log(4)
})
p.then(data => {
console.log(data)
})
console.log(5)
</script>
</body>
</html>
1.4-第四题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
new Promise((resolve, reject) => {
resolve(1)
new Promise((resolve, reject) => {
resolve(2)
}).then(data => {
console.log(data)
})
}).then(data => {
console.log(data)
})
console.log(3)
</script>
</body>
</html>
1.5-第五题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
setTimeout(() => {
console.log(1)
}, 0)
new Promise((resolve, reject) => {
console.log(2)
resolve('p1')
new Promise((resolve, reject) => {
console.log(3)
setTimeout(() => {
resolve('setTimeout2'); //'混淆你的'
console.log(4)
}, 0)
resolve('p2')
}).then(data => {
console.log(data); //'p2'
})
setTimeout(() => {
resolve('setTimeout1'); //'混淆你的'
console.log(5)
}, 0)
}).then(data => {
console.log(data); //'p1'
})
console.log(6)
</script>
</body>
</html>
1.6-第六题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
console.log(1);
async function fnOne() {
console.log(2);
await fnTwo(); // 右结合先执行右侧的代码, 然后等待
console.log(3);
}
async function fnTwo() {
console.log(4);
}
fnOne();
setTimeout(() => {
console.log(5);
}, 2000);
let p = new Promise((resolve, reject) => { // new Promise()里的函数体会马上执行所有代码
console.log(6);
resolve();
console.log(7);
})
setTimeout(() => {
console.log(8)
}, 0)
p.then(() => {
console.log(9);
})
console.log(10);
</script>
<script>
console.log(11);
setTimeout(() => {
console.log(12);
let p = new Promise((resolve) => {
resolve(13);
})
p.then(res => {
console.log(res);
})
console.log(15);
}, 0)
console.log(14);
</script>
</body>
</html>
1.7-第七题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
console.log(1)
new Promise((resolve, reject) => {
resolve(2)
}).then(res => {
console.log(res)
})
setTimeout(() => {
console.log(3)
})
console.log(4)
</script>
<script>
console.log(5)
</script>
<script>
new Promise((resolve,reject)=>{
resolve(6)
}).then(res=>{
console.log(res)
})
setTimeout(()=>{
console.log(7)
})
console.log(8)
</script>
<script>
console.log(9)
setTimeout(()=>{
console.log(10)
})
</script>
</body>
</html>
本文含有隐藏内容,请 开通VIP 后查看