开始正式学习promise之前,首先由几个基本概念来引入:
进程和线程
- 进程:程序的运行环境(相当于工厂的厂房)
- 线程:实际进行运算的东西(相当于工人)
同步
- 通常情况代码都是自上而下一行一行执行的
- 前边的代码不执行后边的代码也不会执行
- 同步的代码执行会出现阻塞的情况
- 一行代码执行慢会影响其他代码的执行
解决同步问题
- Java、Python通过多线程来解决
- node.js通过异步方式解决
function sum(a, b) {
const begin = Date.now()
while(Date.now() - begin < 10000) {}
//此时的while为同步代码
return a + b
}
console.log("111111")
const result = sum(123, 456)
console.log(result)
console.log("111111")
此时程序输出为
111111
579 (10s后输出)
111111 (10s后输出)(此时这段代码被阻塞)
异步
- 一段代码的执行不会影响到其他的程序执行
function sum(a, b) {
setTimeout(() => {
return a + b
}, 10000)
//此时的setTimeout为异步代码,setTimeout等待10s后将任务挂载到任务队列里
}
console.log("111111")
const result = sum(123, 456)
console.log(result)
console.log("111111")
111111
undefined (由于sum()里没有返回值,所以返回undefined)
111111
- 异步的问题:异步的代码无法通过return来设置返回值
- 利用回调函数来解决
function sum(a, b, cb) {
setTimeout(() => {
cb(a + b)
}, 10000)
}
console.log("111111")
sum(123, 456, (result)=>{
console.log(result)
})
console.log("111111")
111111
111111
579(10s后返回)
- 异步的特点:
- 不会阻塞其他代码的执行
- 需要通过回调函数来返回结果
如果要求上一步结果再加一个数的结果呢?
sum(123, 456, (result)=>{
sum(result, 777, (result)=>{
console.log(result)
})
})
若果一直加下去,就会引起回调地狱,造成代码可读性较差
- 基于回调函数的异步带来的问题
- 代码的可读性差
- 可调式性差
- 解决问题:
- 需要一个东西,可以代替回调函数来给我们返回结果
- Promise横空出世:Promise是一个 可以用来存储数据的对象,Promise存储数据的方式比较特殊,这种特殊方式使得Promise可以用来存储异步调用的数据
Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("123")
},2000)
})
-
Promise简单使用
- 创建Promise时,构造函数中需要一个函数作为参数
- Promise构造函数的回调函数,会在创建Promise时调用,调用时会有两个参数传递进去 ,分别是resolve和reject
- resolve和reject是两个函数,通过这两个函数可以向Promise中存储数据
- resolve在执行正常时存储数据,reject在执行错误时存储数据
- 通过函数来向Promise中存储数据,好处就是可以用来添加异步调用的数据
-
从Promise中读取数据
- 可以i通过Promise的实例方法then来读取Promise中存储的数据
- then需要两个回调函数作为参数,回调函数用来获取Promise中的数据
——通过resolve存储的数据,会调用第一个函数返回,可以在第一个函数中编写处理数据的代码
——通过reject存储的数据或者出现异常时,会调用第二个函数返回,可以在第二个函数中编写处理异常的代码
const promise = new Promise((resolve, reject) => {
resolve("123")
})
promise.then((result) => {
console.log("1", result)
}, (reason) => {
console.log("2", reason)
})
1 123
-
Promise中维护了两个隐藏属性:
- PromiseResult:用来存储数据
- PromiseState:记录Promise的三种状态
pending(进行中)
fulfilled(完成)
rejected(拒绝,出错了)state只能修改一次,修改之后永远不会变
- 流程:
当Promise创建时,PromiseState初始值为pending;
当通过resolve存储数据时 PromiseState 变为fulfilled(完成), PromiseResult变为存储的数据;
当通过reject存储数据或出错时,PromiseState 变为rejected(拒绝,出错了),PromiseResult变为存储的数据或异常对象当我们通过then读取数据时,相当于为Promise设置了回调函数,
如果PromiseState变为fulfilled,则调用then的第一个回调函数来返回数据
如果PromiseState变为rejected,则调用then的第二个回调函数来返回数据
const promise2 = new Promise((resolve, reject) => {
reject("哈哈")
})
promise2.catch(reason => {
console.log(222222)
})
222222
- catch() 用法和then()类似,但是只需要一个回调函数作为参数
- catch()中的回调函数只会在Promise被拒绝时才调用
- catch()相当于then(null, reason => {})
- catch()就是一个专门处理Promise异常的方法
const promise2 = new Promise((resolve, reject) => {
reject("哈哈")
})
promise2.finally(() => {
console.log("无论如何都会执行")
})
无论如何都会执行
-
finally()
- 无论是正常存储数据还是出现了异常,finally总会执行
- 但是finally的回调函数中不会收到数据
- finally()通常用来编写一些无论成功与否都要执行的代码
-
promise中的then\catch\finally这三个方法调用时都会返回一个新的Promise
-
对Promise进行链式调用时,Promise后边的方法(包括then和catch)读取的是上一步的执行结果,如果上一步的执行结果不是当前想要的结果,则跳过当前的方法
-
当Promise出现异常时,而整个调用链中没有出现catch,则异常会向外抛出
Promise的一些静态方法
- Promise.resolve()创建一个立即完成的Promise
Promise.resolve(10).then(r => console.log(r))
//上下等价
new Promise((resolve, reject) => {
resolve(10)
}).then(r => console.log(r))
- Promise.reject()创建一个立即拒绝的Promise
Promise.reject(10).catch(r => console.log(r))
new Promise((resolve, reject) => {
reject(10)
}).catch(r => console.log(r))
- Promise.all([…])同时返回多个Promise的执行结果 ,其中有一个报错,就返回错误
function sum(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a + b)
}, 1000)
})
}
Promise.all([sum(4, 5), sum(6, 7), sum(7, 8)]).then(r => console.log(r))
[ 9, 13, 15 ]
function sum(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a + b)
}, 1000)
})
}
Promise.all(
[sum(4, 5),
sum(6, 7),
sum(7, 8)
Promise.reject("123")]).then(r => console.log(r))
执行报错
- Promise.allSettled([…])同时返回多个Promise的执行结果()无论成功与否都会返回数据,返回的是一个对象数组
成功 {status:“fulfilled”, value:result}
失败{status:“rejected”, reason:error}
- Promise.race([…])返回执行最快的Promise,不考虑对错
function sum(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a + b)
}, 1000)
})
}
Promise.race([
Promise.resolve(0), //立即执行
sum(4, 5),
sum(5, 6),
sum(7, 8)
]).then(r => console.log(r))
0
function sum(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a + b)
}, 1000)
})
}
Promise.race([
Promise.reject(0),
sum(4, 5),
sum(5, 6),
sum(7, 8)
]).then(r => console.log(r))
.catch(e => console.log(e))
0
- Promise.any([…])返回执行最快的完成的Promise(即考虑对错,错的不执行,只有全是错的才会报错)
function sum(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a + b)
}, 1000)
})
}
Promise.any([
Promise.reject(0),
sum(4, 5),
sum(5, 6),
sum(7, 8)
]).then(r => console.log(r))
.catch(e => console.log(e))
9
宏任务和微任务
- js是单线程的,它的运行是基于事件循环机制(event loop)
- 调用栈:执行的代码
- 消息队列(任务队列):将要执行的代码
- 当调用栈里的代码执行完后消息队列中的代码才会进入调用栈进行执行
- JS中任务队列有两种
- 宏任务队列(大部分代码都去宏任务队列中排队)
- 微任务队列(Promise的回调函数then/catch/finally)
- JS事件循环机制流程:1——调用栈;2——微任务;3——宏任务
//定时器的作用是间隔一段时间后,将函数放入到任务队列中,宏任务
setTimeout(() => {
console.log(1)
})
//微任务
Promise.resolve(1).then(() => {
console.log(2);
})
//位于全局作用域,在调用栈中
console.log(3)
//输出3 2 1
- queueMicrotask() 用来向微任务队列中添加一个任务
async和await
function fn() {
return Promise.resolve(10)
}
//上下二者等价
//通过async可以创建一个异步函数,其返回值会自动封装到一个Promise中
async function fn2() {
return 10
}
function sum(a, b) {
return Promise.resolve(a + b)
}
async function fn3() {
let result = await sum(123, 456)
console.log(result)
}
fn3()
579
- 当我们调用await去调用异步函数时,他会暂停代码的运行,直到异步代码执行有结果时,才会将结果返回
- 注意,await只能用于async声明的异步函数中或es模块的顶级作用域中
- await阻塞的只是异步函数内部的代码,不会影响外部代码
- 通过await调用异步代码时,需要通过try-catch来处理异常
- 如果async声明的函数里没有写await,那么它里边就会依次执行,如下边程序所示:
async function fn4() {
console.log(1)
console.log(2)
console.log(3)
}
// function fn5() {
// return new Promise((resolve) => {
// console.log(1);
// console.log(2);
// console.log(3);
// })
// }
//fn4()与fn5()等价
fn4()
console.log(4)
//1 2 3 4
- 当我们使用await调用函数后,当前函数后边的所有代码会在当前函数执行完毕后被放入到微任务队列中
async function fn6() {
console.log(1);
await console.log(2);
console.log(3);
}
fn6();
console.log(4);
///1 2 4 3
//等价于如下代码
function fn7() {
return new Promise((resolve) => {
console.log(1)
//加了await
console.log(2)
resolve()
}).then(r => {
console.log(3)
})
}
fn7()
console.log(4);
(ps:老师的网站怎么找不到了sad)