浅谈Promise理解、实现手写Promise

  • 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

})














结语:

本文章总结:

bilibili node.js完全指南 李立超讲师

李立超老师个人网站-Promise

建议参考阅读:
单线程的JavaScript是如何实现异步的
JavaScript:彻底理解同步、异步和事件循环(Event Loop)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值