promise
1. axios封装参数提取
javascript
function myAxios(config){
return new Promise((resolve, reject) => {
// 解构 ==> 可以设置默认值
const {url, method = 'get'} = config
// 原生ajax 五步走
// 1. 实例化xhr
const xhr = new XMLHttpRequest()
// 2. 设置方法和接口
xhr.open(method, url)
// 3. 请求头
// xhr.setRequestHeader(key, val)
// 4. 注册回调函数
xhr.addEventListener('load', () => {
if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){
console.log()
resolve(JSON.parse(xhr.response))
} else {
reject('请求失败')
}
})
// 5. 发送请求
xhr.send()
})
}
const api = 'http://ajax-api.itheima.net/api/news'
myAxios({
url:api,
// method:'get'
}).then(res => {
console.log(res)
})
ajax-添加post
javascript
<h2>post传递JSON数据给后端</h2>
<div class="wrap">
<button class="get">获取图书</button>
<button class="add">新增图书</button>
<button class="edit">修改图书</button>
<button class="delete">删除图书</button>
</div>
<script>
/*
参数是一个对象config
url
method
data:{} 通过请求体传递的数据写在data属性里面
*/
function myAxios(config){
return new Promise((resolve, reject) => {
const {url, method = 'get', data} = config
// 原生ajax 五步走
// 1. 实例化xhr
const xhr = new XMLHttpRequest()
// 2. 设置方法和接口
xhr.open(method, url)
// 3. 请求头
xhr.setRequestHeader('content-type', 'application/json')
// 4. 注册回调函数
xhr.addEventListener('load', () => {
if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){
console.log()
resolve(JSON.parse(xhr.response))
} else {
reject('请求失败')
}
})
// 5. 发送请求
xhr.send(JSON.stringify(data))
})
}
// apifox:
// https://apifox.com/apidoc/shared-fa9274ac-362e-4905-806b-6135df6aa90e/api-25018003
// GET
const api_get = 'http://ajax-api.itheima.net/api/books'
// POST JSON格式 bookname/author/publisher
const api_add = 'http://ajax-api.itheima.net/api/books'
// 修改图书 PUT JSON格式 id
const api_edit = 'http://ajax-api.itheima.net/api/books/{id}'
// 删除图书 DELETE id
const api_del = 'http://ajax-api.itheima.net/api/books/{id}'
// const btn_get = document.querySelector('.get')
// const btn_add = document.querySelector('.add')
// const btn_edit = document.querySelector('.edit')
// const btn_del = document.querySelector('.delete')
// 事件委托绑定事件
const box = document.querySelector('.wrap')
box.addEventListener('click', (e) => {
if(e.target.className === 'get'){
// console.log(111)
myAxios({
url:api_get,
}).then(res => {
console.log(res)
})
}
})
</script>
完整代码
javascript
function myAxios(config){
return new Promise((resolve, reject) => {
const {url, method = 'get', data} = config
// 原生ajax 五步走
// 1. 实例化xhr
const xhr = new XMLHttpRequest()
// 2. 设置方法和接口
xhr.open(method, url)
// 3. 请求头
xhr.setRequestHeader('content-type', 'application/json')
// 4. 注册回调函数
xhr.addEventListener('load', () => {
if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){
console.log(1111)
resolve(JSON.parse(xhr.response))
} else {
reject('请求失败')
}
})
// 5. 发送请求
xhr.send(JSON.stringify(data))
})
}
// apifox:
// https://apifox.com/apidoc/shared-fa9274ac-362e-4905-806b-6135df6aa90e/api-25018003
// GET 获取图书
const api_get = 'http://ajax-api.itheima.net/api/books'
// 新增图书
// POST JSON格式 bookname/author/publisher
const api_add = 'http://ajax-api.itheima.net/api/books'
// 修改图书 PUT JSON格式 id
const api_edit = 'http://ajax-api.itheima.net/api/books'
// 删除图书 DELETE id
const api_del = 'http://ajax-api.itheima.net/api/books'
// const btn_get = document.querySelector('.get')
// const btn_add = document.querySelector('.add')
// const btn_edit = document.querySelector('.edit')
// const btn_del = document.querySelector('.delete')
// 事件委托绑定事件
const box = document.querySelector('.wrap')
box.addEventListener('click', (e) => {
if(e.target.className === 'get'){
// console.log(111)
myAxios({
url:api_get,
}).then(res => {
console.log(res)
})
}
if(e.target.className === 'add'){
myAxios({
url:api_add,
method:'post',
data:{
bookname:'新增的',
author:'xx',
publisher:'hh'
}
}).then(res => {
console.log(res)
})
}
if (e.target.className === 'edit'){
myAxios({
url:`${api_edit}/325`,
method:'put',
data:{
bookname:'新增的修改',
author:'xx',
publisher:'hh'
}
}).then(res => {
console.log(res)
})
}
if (e.target.className === 'delete'){
myAxios({
url:`${api_del}/318`,
method:'delete',
}).then(res => {
console.log(res)
// alert('删除成功')
}).catch(err => {
console.log(err)
})
}
})
2. promise.then() 的返回值
javascript
const p = new Promise((resolve, reject) => {
// 一些异步代码
resolve('success')
})
// console.log(p)
// 1. promise.then() 返回一个promise对象
const res1 = p.then(res => {
console.log(res)
})
res1.then(res => {
console.log(res)
})
// console.log(res)
// 2. 如果p.then中回调函数,返回了一个非promise的值,
// 则新的promise的将使用这个值作为成功的结果
const res2 = p.then(res => {
console.log(res)
return 'second success'
})
// console.log(res)
// 3. 如果回调函数本身返回了一个promise对象,则then方法调用后返回的新promise
// 与该promise对象采用相同的成功结果或失败原因
const res3 = p.then(res => {
console.log(res)
return new Promise((resolve,reject) => {reject(1)})
})
// console.log(res)
===> 一句话总结 p.then() 返回的是一个promise对象,
// 1. then的回调中没有return ,那么promise成功的结果为undefined
// 2. then中return了一个非promise对象,新promise以该值作为成功的结果
// 3. then中return了一个promise对象,新promise 成功或失败与它相同
3. promsie.then() 第二个参数
javascript
const p = new Promise((resolve,reject) => {
setTimeout(()=>{
if(Math.random() > 0.5){
resolve('success')
} else {
reject('failed')
}
}, 1000)
})
// catch的方式用的更多一些推荐使用
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// promise.then() 可以接收两个函数作为参数,
// 第一个函数是promise状态为fulfilled时调用
// 第二个回调函数是promise对象为reject时调用
p.then((res)=>{
console.log(res)
}, (err)=>{
console.log(err)
})
4. Promise.resolve()
javascript
// Promsie.resolve() promise的静态方法
// ==> 调用这个方法,返回的就是一个promsie对象
// const p = Promise.resolve(1)
// const p = Promise.resolve('123')
// 1. 如果不传参数 => 返回的就是一个成功态的promise对象,结果为undefined
const p = Promise.resolve()
console.log(p)
// 2. 如果传入的value是一个普通的值或者对象,返回一个新的promise对象,
// 并且它的状态是已完成/成功的, 值就是传入的这个值
const p = Promise.resolve({name:'果果'})
console.log(p)
p.then(res => {
console.log(res)
})
Promise.resolve(1)
// ==> 等价于
new Promise(resolve => resolve(1))
// 3. 如果传入的value就是一个promise对象
// Promise.resolve将原封不动的,返回这个promise
const p2 = new Promise((resolve, reject) => {
reject('失败了')
})
const p3 = Promise.resolve(p2)
console.log(p3)
5. Promise.reject()
javascript
// Promise.reject() 返回一个新的promise对象,并且状态是失败,结果就是传入的参数
const p = Promise.reject('出错了')
console.log(p)
// 等同于
// const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, err => {
console.log(err)
})
Promise.reject('出错了').catch(err => {
console.log(err)
})
6. Promise.all()
javascript
// Promsie.all() 静态方法
// 1. Promise.all接收一个promise数组作为参数,返回一个新的promise对象。
// const p = Promise.all([p1, p2, p3])
// 最后p的状态,由p1,p2,p3决定
// 分两种情况
// 2.1 当p1,p2,p3都为fulfilled状态时,p的状态才会变成fulfilled(成功态、完成态)
// p1,p2,p3的返回值,会挨个(按顺序)的组成一个新的数组,作为p的结果
// 2.2
// 只要p1, p2, p3 有一个被rejected了,失败了,p的状态就会变成rejected,
// 第一个被rejected的promise的值(失败的原因),会作为p的结果
// ==> p.catch(err => console.log(err))
const p1 = Promise.resolve(111)
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(222)
}, 2000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333)
}, 3000);
})
const p4 = Promise.reject('失败了4')
const p5 = Promise.reject('失败了5')
// 1. 所有的promise都成功了
const p = Promise.all([p1, p2, p3])
p.then(res => {
console.log(res)
})
console.log(p)
// 2. 有一个promise失败了
const p21 = Promise.all([p1, p2, p4])
console.log(p11)
p11.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// 3. 有两个promise失败了, 第一个失败的值会作为promise.all的结果
const p22 = Promise.all([p1, p4, p5])
console.log(p22)
p22.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
07 Promise.race()
javascript
// Promise.race() 接收一个promise数组
// 只要p1, p2, p3之中 有一个实例率先改变状态,p的状态就跟着改变
// race返回一个promise,它可以是完成态(成功态fulfilled),也可以是失败(rejected)
// race 竞赛,只认第一名
// const sleep = (ms) => {
// return new Promise((resolve) =>{
// setTimeout(() => {
// resolve(ms)
// }, ms);
// })
// }
const sleep1 = s => new Promise(resolve => setTimeout(resolve, s * 1000))
// 上面这个返回值是undefined,1s后 => 成功态
const sleep2 = s => new Promise(resolve => setTimeout(resolve(s), s * 1000))
// 上面这个resolve(s) 立即执行了 , 立即得到1000结果
const sleep = s => new Promise(resolve => setTimeout(() => { resolve(s) }, s * 1000))
// sleep(1000).then(res => {
// console.log(res)
// console.log(222)
// })
// sleep(1000).then(res => {
// console.log(res)
// })
const p1 = sleep(1)
const p2 = sleep(6)
const p3 = sleep(5)
const p4 = Promise.reject('error')
const p = Promise.race([p1, p2, p3, p4])
console.log(p)
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
08 Promise.all和Promise.race
javascript
const api_books = 'http://ajax-api.itheima.net/api/books'
const api_news = 'http://ajax-api.itheima.net/api/news'
const api_robot = 'http://ajax-api.itheima.net/api/robot?spoken=你不好'
// Promise.all()
const fetch_book = fetch(api_books).then(res => res.json())
// console.log(fetch_book)
const fetch_news = fetch(api_news).then(res => res.json())
// console.log(fetch_news)
const fetch_robot = fetch(api_robot).then(res => res.json())
// console.log(fetch_robot)
// 所有的请求都成功了,返回给P
const p = Promise.all([fetch_book,fetch_news,fetch_robot])
console.log(p)
// Promise.race()
// 谁最快,最快的那个拿过来
const p2 = Promise.race([fetch_book,fetch_news,fetch_robot])
console.log(p2)
// all获取所有,race获取第一个
09 Promise.allSettled
想知道每一个promise的结果时,可以使用promise.allSettled
javascript
// 接收一个Promise数组,在所有promise对象都已经fulfilled或rejected后
// 返回一个返回每个promise结果的对象数组.
// 每个对象有三个属性
// status => promsie的状态: fulfilled / rejected
// value =>成功的结果,如果rejected,undfined
// reason =>失败的原因,如果fulfilled,undefined
const sleep = s => new Promise(resolve => setTimeout(() => { resolve(s) }, s * 1000))
const promises = [
Promise.resolve(1),
Promise.reject('error'),
Promise.resolve(3),
sleep(5)
]
Promise.allSettled(promises).then(res => {
// 等结果--> 有了结果,才执行.then的回调
console.log(res)
})
10 Promise.any()
等待第一个promise是成功的状态, Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。
javascript
// 等待第一个成功的状态,如果都失败了, 返回一个失败状态的promise的对象,值是所有的都失败
const sleep = s => new Promise((resolve,reject) => setTimeout(() => { reject('error') }, s * 1000))
const promises = [
Promise.reject('error'),
// sleep(5),
Promise.reject('666'),
// sleep(3)
// Promise.reject('333')
sleep(3)
]
const res = Promise.any(promises)
console.log(res)
res.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// Promise.any(promises).then(res => {
// // 等结果--> 有了结果,才执行.then的回调
// console.log(res)
// })
javascript
Promise.all() ==> 等待所有的成功状态
Promise.race() ==> 等待第一个promise的状态确定
Promise.allSettled() ==> 等带每一个promise的状态
Promise.any() ==> 等待第一个成功的状态确定
JS是一门单线程语言
javascript
// 1. JS 是一门单线程的语言
// 也就是说,同一段时间只能做一件事,前一件事情做完了,才做下一件事情。
// 为什么JS是一门单线程的语言呢?
// JS设计之初为了操作DOM元素的, 我们不能同时去添加或者删除同一个DOM.
// 代码是从上到下运行
// 2. 线程 和 进程
// ===> 简单理解
// 进程 process ==> 可以理解为正在运行着的程序
// 线程 thread ==> 程序中的一个执行单元或执行路径。
// 在一个程序中,可能会有多个线程同时执行不同的任务
// 如果把 进程 ==> 火车 , 线程 ==> 一节一节的车厢 。
// 线程进程的特点:
// 1. 一个进程可以有多个线程
// 2. 线程必须依附进程才能运行
// 3. 线程之间共享进程中的数据。
// 一个正在生产着运行着的工厂 。。 ==> 进程。
// 可以将进程比喻成一个正在生产着运行着的工厂,而线程则是工厂里的工人。
// 在工厂中,不同的工人可以同时处理不同的任务,而且工人之间可以相互协作,共同完成整个生产过程。
// 3. 同步和异步
// 同步:同一时间只有一个任务再执行,前一个任务执行完,才能执行后一个任务。 串联
// 异步:可以同时执行多个任务,可以提高程序的性能。 并联
浏览器架构
javascript
// 目前:Chrome采用多进程架构
打开一个页面,会至少有四个进程
1. 浏览器(Browser)主进程
2. GPU 进程
3. 网络(NetWork)进程
4. 渲染进程 (Renderer Process)
5. 如果有插件在运行,还会有插件进程
----
1. 1 个浏览器(Browser)主进程
===> 主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
2. 1 个GPU 进程
===> 用于加速渲染和处理网页中的图形和动画效果,
---> 一开始是为了3D CSS绘制,之后网页,Chrome UI界面都采用GPU绘制
3. 1 个网络(NetWork)进程:
===> 负责处理网络请求,如HTTP请求、WebSocket连接等。
4. 多个渲染进程 (Renderer Process):
===> 负责处理一个选项卡Tab的所有页面渲染任务。
5. 多个插件进程。(可能无)
===> 主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保
证插件进程崩溃不会对浏览器和页面造成影响。
// JavaScriptV8 引擎和JS代码都运行在 渲染进程中!
// Chrome 会为每个 Tab 标签创建一个渲染进程
事件循环
javascript
console.log(1)
document.addEventListener('click', function () {
console.log(4)
})
console.log(2)
setTimeout(function () {
console.log(3)
}, 3000)
// 思考
// 异步任务一般由回调函数组成。
// 1. dom事件 click、load , resize
// 2. ajax/fetch 网络请求
// 3. setTimeout / setInterval
/* ==================== 事件循环低配版理解 ===================== */
// 1. 首先判断JS任务是同步的还是异步的,同步任务会在主线程的执行栈上依次执行。(顺序执行)
// 2. 异步任务会提交给异步进程处理(click/fetch),当满足触发条件后,异步进程会将异步任务(回调)
// 推到消息队列(任务队列)中
// 3. 当主线程上的所有的所有的,所有的同步任务执行完之后,就会去消息队列(任务队列中)看有没有
// 可以执行的异步任务,如果有,拿到主线程上执行.
// 执行完之后再去消息队列中查看,如果没有,等,依次不断的循环
// EventLoop
javascript
console.log(0);
setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
console.log(6)
}).then(function(val){
console.log(val);
})
console.log(4);
事件循环 => 宏微任务
javascript
console.log(0);
// 下一个宏任务的开始
setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
// 同步执行的!
console.log(2)
resolve(3)
console.log(6)
}).then(function(val){
console.log(val);
})
console.log(4)
// 我们的任务还分为 宏任务 macroTask 和 微任务 microTask
// 宏任务
// 1. script代码块
// 2. setTimeout / setInterval
// 3. setImmediate
// 微任务
// 1. promise.then() / promise.catch()
// 2. async / await
// 3. MutationObeserver ==> 监听dom的改变的(Vue源码中有)
// 4. process.nextTick (node)
// 在当前事件循环中,微任务的优先级高,比下一个宏任务的优先级高
// 微任务是属于(包含于)宏任务里面的嘛? 是
// 1. 一个宏任务中,可以包含多个微任务
// 2. 每执行完一个宏任务,就会清空当前的微任务队列中的所有微任务。
// 执行宏任务,清空当前宏任务中产生的所有微任务,然后再执行下一个宏任务,再清空所有的微任务。
/* ==================== 事件循环 宏微任务 ===================== */
// 1. 首先,我们的script代码块,可以看做是一个宏任务,开始第一个Tick事件循环。
// 2. 先执行当前任务中的所有同步代码,
// 3. 如果遇到宏任务,就放到宏任务队列中等待执行,如果遇到微任务,就放到微任务队列中。
// 4. 当主线程执行完所有的同步代码,首先,去微任务队列中清空当前事件循环的所有微任务
// (表示这一轮Tick事件循环结束)
// 5. 再去执行下一个宏任务 (下一个宏任务的开始)
我们经常说微任务的优先级比宏任务高?怎么理解?
==> 在本轮事件循环中的微任务比下一次事件循环中的宏任务优先级高
我们可以把宏微任务关系想象成 拔出萝卜带出泥,宏任务就是萝卜,微任务就是萝卜上的泥。
我们每次拔萝卜,都要把萝卜上的泥清理掉,再去拔下一个萝卜~~~
javascript
function wait() {
return new Promise((resolve) => {
setTimeout(() => {
console.log(111)
const p = new Promise(resolve => {
resolve(6)
})
p.then(res => {
console.log(res)
})
})
console.log(222)
resolve(666)
}, 1000)
}
wait().then((res) => {
console.log(res)
console.log(3)
})
async / await
javascript
// async和await
// 作用:是异步终极解决方案——》是一对关键词,需要配合使用
// async关键字,
// 1. async用于修饰一个函数,表示该函数是异步的,=> 返回一个promsie对象
// 如果在async函数内部没有 await,此时async是没有意义的,函数内部就全是同步内容
// 如果在async函数内部遇到了await,await下面那一行的代码会以异步处理,相当于.then的回调
// 2. async函数内部return语句返回的值,会成为then方法回调函数的参数。
// await关键字:
// 1. await必须在async函数内使用,写在普通函数内会报错
// 2. await后面一般会跟着一个promise对象, 返回该对象的结果(成功或失败)
// 如果不是 Promise 对象,就直接返回对应的值。
// 3. await后面的Promise对象,运行结果可能是rejected,那么会报错
// 所以最好把await命令放在try...catch代码块中。
try {
放可能会出错的语法
} catch(err) {
一旦try中的代码出错,会被catch捕获到,其中的参数e就是错误信息
}
javascript
// async和await
// 作用:是异步终极解决方案——》是一对关键词,需要配合使用
// async关键字,
// 1. async用于修饰一个函数,表示该函数是异步的,=> 返回一个promsie对象
async function fn(){
// 内部按理说,应该写一些异步的代码
return 'hello async'
}
console.log(fn())
// 2.1 如果在async函数内部没有 await,此时async是没有意义的,函数内部就全是同步内容
async function foo(){
let num = 100
console.log(num)
}
foo()
console.log(200)
// 2.2 如果在async函数内部遇到了await,await下面那一行的代码会以异步处理,相当于.then的回调
async function foo(){
let num = await 100 // ==> 这一行是同步执行的,
// await下面的都相当于是放到了.then()的回调函数中
console.log(333)
}
// ===> 相当于
function foo(){
return Promise.resolve(100).then(num => {
console.log(num)
console.log(222)
console.log(333)
})
}
// 3. async函数内部return语句返回的值,会成为then方法回调函数的参数。
async function fn(){
// 内部按理说,应该写一些异步的代码
return 'hello async'
}
fn().then(res => {
console.log(res)
})
async有多种形式
javascript
// 1. 函数声明
async function foo(){}
// 2. 函数表达式
const bar = async function(){}
// 3. 对象的方法
const obj = {
name:'jianguo',
async sayHi(){
console.log(111)
}
}
// 4. 箭头函数
const foo = async () => {}
await 关键字
javascript
// await关键字:
// 1. await必须在async函数内使用,写在普通函数内会报错
async function fn(){
let num = await 100
console.log(num)
}
fn()
// 2. await后面一般会跟着一个promise对象, 返回该对象的结果(成功或失败)
// 如果不是 Promise 对象,就直接返回对应的值。
const sleep = s => new Promise(resolve => setTimeout(() => resolve(s), s * 1000))
// const p = sleep(1)
// console.log(p)
// p.then(res => {
// console.log(res)
// })
async function fn(){
let res = await sleep(3)
// 下面的代码,相当于.then(res => {})
console.log(res)
}
fn()
const api_books = 'http://ajax-api.itheima.net/api/books'
const api_news = 'http://ajax-api.itheima.net/api/news'
// // 获取新闻列表
// fetch(api_news).then(res => res.json()).then(res => {
// console.log(res)
// })
// // 获取图书列表
// fetch(api_books).then(res => res.json()).then(res => {
// console.log(res)
// })
//Q:先获取新闻,等新闻的结果回来了之后,我再请求图书列表 ?
// 获取新闻列表
fetch(api_news).then(res => res.json()).then(res => {
console.log(res)
// 获取图书列表
fetch(api_books).then(res => res.json()).then(res => {
console.log(res)
})
})
;(async function(){
// await的意思就是等待
let res1 = await fetch(api_news).then(res => res.json())
// await 会阻塞后面代码的执行
console.log(res1)
let res2 = await fetch(api_books).then(res => res.json())
console.log(res2)
})()
javascript
async function async1() {
// 这里的代码是同步的
console.log('async1 start')
await async2() // 这里要执行,同步,不等待
// 相当于放到了.then() 异步 微任务
console.log('async1 end')
console.log(666)
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function() {
console.log('setTimeout')
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1')
resolve();
}).then(function() {
console.log('promise2')
});
console.log('script end')
// script start
// async1 start
// async2
// promise1
// script end ==> 所有的同步代码执行完了 , 清空微任务队列
// async1 end 666
// promise2 ==> 当前事件循环结束 Tick结束
// setTimeout
我们经常说微任务的优先级比宏任务高?怎么理解?
==> 在本轮事件循环中的微任务比下一次事件循环中的宏任务优先级高
我们可以把宏微任务关系想象成 拔出萝卜带出泥,宏任务就是萝卜,微任务就是萝卜上的泥。
我们每次拔萝卜,都要把萝卜上的泥清理掉,再去拔下一个萝卜~~~
javascript
console.log(1)
setTimeout(function () {
console.log(2)
new Promise(function (resolve) {
console.log(3)
resolve()
}).then(function () {
console.log(4)
})
})
const p = new Promise((resolve, reject) => {
console.log(5)
resolve() // 标记为成功
console.log(6)
})
p.then(data => {
console.log(7)
})
console.log(8)
// 1 5 6 8 7 / 2 3 4