Promise 是什么
Promise 原理
Promise 执行流程
Promise 手写
(一 )Promise 是什么?
1.前言:
异步代码最大的问题就是回调函数。由于是异步执行,异步函数无法直接通过返回值来返回执行结果,要想取得结果必须通过回调函数。这也就带来了一个问题-回调地狱
2 .Promise 是什么?
Promise
- Promise可以帮助我们解决异步中的回调函数的问题
- Promise就是一个用来存储数据的容器
它拥有着一套特殊的存取数据的方式
这个方式使得它里边可以存储异步调用的结果
const promise = new Promise((resolve, reject) => {
// resolve 和 reject 是两个函数,通过这两个函数可以向Promise中存储数据
// resolve在执行正常时存储数据,reject在执行错误时存储数据
//resolve("resolve返回的数据")
// reject("reject返回的数据")
})
这里涉及到 回调地狱 同步 异步
3.Promise 拿取异步函数的结果
从Promise中读取数据
- 可以通过Promise的实例方法then来读取Promise中存储的数据
- then需要两个回调函数作为参数,回调函数用来获取Promise中的数据
通过resolve存储的数据,会调用第一个函数返回,
可以在第一个函数中编写处理数据的代码
通过reject存储的数据或者出现异常时,会调用第二个函数返回
可以在第二个函数中编写处理异常的代码
promise.then((result) => {
console.log("1", result)
}, (reason) => {
console.log("2", reason)
})
(二) Promise 原理
原理
在Promise中维护着两个隐藏的值PromiseResult和PromiseState,PromiseResult是Promise中真正存储值的地方,在Promise中无论是通过resolve、reject还是报错时的异常信息都会存储到PromiseResult中。PromiseState用来表示Promise中值的状态,Promise一共有三种状态:pending、fulfilled、rejected。pending是Promise的初始化状态,此时Promise中没有任何值。fulfilled是Promise的完成状态,此时表示值已经正常存储到了Promise中(通过resolve)。rejected表示拒绝,此时表示值是通过reject存储的或是执行时出现了错误。
当我们调用Promise的then方法时,相当于为Promise设置了一个回调函数,换句话说,then中的回调函数不会立即执行,而是在Promise的PromiseState发生变化时才会执行。如果PromiseState从pending变成了fulfilled则then的第一个回调函数执行,且PromiseResult的值作为参数传递给回调函数。如果PromiseState从pending变成了rejected则then的第二个回调函数执行,且PromiseResult的值作为参数传递给回调函数。
(三) Promise 执行流程
Promise的执行原理
- Promise在执行,then就相当于给Promise了回调函数
当Promise的状态从pending 变为 fulfilled时,
then的回调函数会被放入到任务队列中
这里涉及到调用栈 任务队了 微任务 宏任务
注: 这不在详细举列 Promise 的静态方法 和实列方法 以及链式调用 以及 不同的实列方法的运用的注意事项和怎样用和实列方法的返回的Promise的对象 等等特性
(四) Promise 手写
const PROMISE_STATE = {
PENDING: 0,
FULFILLED: 1,
REJECTED: 2
}
class MyPromise {
// 04 接受结果
#result
#status = PROMISE_STATE.PENDING//04 resolve reject 只能在一次的实列中调用一次 即状态发生一次变化 后面的重负调用存储数据会忽略 所以用一个变量去记录promise 的状态 pending fulfilled rejected 进行中 完成 解决 0 1 2
// 06-2
// 创建一个变量来存储回调函数
// #callback // 即当resolve 执行的时候
// 8-02
#callbacks =[]// 即回到函数有多个 ,我们使用数组来存储
// 01-接受传进入的两个参数
constructor(executor) {
// 02-3 绑定this
// executor(this.#resolve, this.#reject)
executor(this.#resolve.bind(this), this.#reject.bind(this))
}
// 02-私有的成功回调 (原型上)这样写会被添加到原型上
// #resolve(value) {
// // 02-1 存入数据
// console.log(value);
// // this.#resolve=value
// // 但是在类的严格模式下 this=undefined
// }
// 02-2 私有的成功回调 (对象自身上)实列方法 使用箭头函数的this this就是实列对象
// #resolve =(value)=> {
// this.#resolve= value
// }
// 02-3 私有的成功回调 更改this的指向
#resolve(value) {
// 04-1 禁止值被重复修改
// 如果state 不等于0 ,说明值已经被修改。函数直接返回
if (this.#status !== PROMISE_STATE.PENDING) return
this.#result = value
this.#status = PROMISE_STATE.FULFILLED //数据填充成功
// 当resolve 执行时,说明数据已经进来了,需要调用then的回调函数 把数据通过回调 函数返回
// 即 怎样让resolve 函数 看到ondFufilled 属性
//6-03 +6-01
// this.#callback && this.#callback(this.#result)
// 8-02
// 调用claabacks 中存储的所有回调函数 下边微任务 ,这里也微任务
queueMicrotask(()=>{
this.#callbacks.forEach(cb=>{
cb()
})
})
}
// 03-私有的失败回调
#reject(reason) {
console.log(this);
}
// 05 -制造取出数据的 写then方法
then(onfufilled, onRejected) {
const promise = new MyPromise((resolve,reject)=>{
// 6-02
if (this.#status === PROMISE_STATE.PENDING) {
// this.#callback = onfufilled // 8-02 这里赋值就没有意思了
// 即this.#callback.push() //8-02往数组里边加东西 加我们的回调函数 onFulfilled 直接加有点小问题 加一个包装之后的函数
this.#callbacks.push(() => {
resolve(onfufilled(this.#result)) //即当箭头函数直执行时 onfufilled 执行 then的回调
})
}
//onfufilled 读取成功的
//onRejected 读取失败的
else if (this.#status === PROMISE_STATE.FULFILLED) {
//说明状态从pending-> fufilled
// 即调用then方法第一个回调 -取得成功的数据 读数据调回调函数
// 5-01 这里的版本是在数据一进来就开始调用了
// onfufilled(this.#result)
// 7-01 这里是在数据一进来放进了微任务队列中 即等调用栈清空了,微任务才执行 ->异步
// then的
queueMicrotask(() => {
resolve(onfufilled(this.#result))
})
/* 06-1
解决需求:then现在只能读取已经存进Promise的数据而不能读取异步存储的数据
这里直接挑明了说:
Promise的执行原理:
Promise在执行,then就相当于给Promise了回调函数
当Promise的状态从pending 变为 fulfilled时,
then的回调函数会被放入到任务队列中
即:6-02
// 当resolve执行时,说明数据已经进来了,需要调用then的回调函数
那么我们就在调用then时,以成功的回调为列 ,让其then的OnFufilled 的即第一个函数存入,等待resolve()所处的异步环境执行完在调用其then的onFufilled 即第一个函数 把值存入变量中 即在resolve调用其存入的callback,即执行then第二个if语句-->拿出数据 就解决了异步需求。
//6-03
上述针对异步,那么当resolve()所处的环境 是同步执行,那么在其then调用时
那么在resolve 中针对同步的完善需要 查看 callback 是否存在,不存在 不执行, 存在 就执行 即在等待状态从pending->fufilled
即this.callback&& this.callback()
*/
/*
7-01 :新的问题又来了 ,即:
then 的回到函数,应该放入带微任务队列中执行,而不是直接调用:
即:
queueMicrotask(()=>{
onfufilled(this.#result)
})
*/
}
})
return promise
}
}
// 创建手写实列
const mp = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("孙悟空")
}, 1000);
resolve("猪八戒")
})
mp.then((value) => {
console.log("第一次数据",value);
})
// console.log(mp);
mp.then((value)=>{
console.log("第二次数据",value);
})
/*
8-01
新的问题又出现了 ,then 时刻一重复调用的,但是我们手写的promise 是把值重复修改了 也就是给onfuilled 赋值了两次 ,所以以最后一次为准;问题: (同步是正常的) ,then多次调用->即同一时刻可能有多个callback
即回调函数可能也有多个 ,把#callback 改成 #callbacks
*/
/*
9-01 新的问题偷来了可以链式调用的前提 是
返回promise对象 及调用之后返回 promise 但是这里我们返回的是undefined
所以 更改如同 9-02
9-02 新的问题又来了
即谁将成为返回的新对象->谁将成为then返回的新Promise 中的数据
即 then中回调函数的返回值 ,会成为新的Promise中的数据
所以:resolve(onfufilled(this.#result))
*/
let result= mp.then((value)=>{
console.log("第二次数据",value);
})
console.log(result);
mp.then((data)=>{
console.log("数据1",data);
return 999
}).then((data)=>{
console.log("数据2",data);//999
})