Promise源码分析(附带Promise的正确打开方式)

本文深入剖析Promise的工作原理,包括其内部状态管理、resolve/reject机制及then/catch方法的实现。同时,探讨Promise的静态成员如all、race、resolve和reject的使用,以及async/await语法糖的应用,帮助读者掌握异步编程的核心技巧。

Promise源码分析

Promise的底层是浏览器实现的,所以我只是实现了里的功能,并非和源码一摸一样。下面为部分源码,完整源码移驾到github中下载

https://github.com/young-monk/my-promise.git

我们首先来定义一个MyPromise类,我在这里使用立即执行函数的方式,防止变量全局污染。

const MyPromise = (() => {
   
   
    //在这里定义内部变量
    return class {
   
   
        //构造器
        constructor(executor) {
   
   
            //...
        }
})()

接下来,定义一些内部使用的变量:我这里使用了Symbol,使用Symbol的原因是改变量只能内部使用

const MyPromise = (() => {
   
   
    //在这里定义内部变量
    const PENDING = "pending"
    const RESOLVED = "resolved"
    const REJECTED = "rejected"
    //使用Symbol是为了仅供内部使用
    const PromiseStatus = Symbol("PromiseStatus")//当前状态
    const PromiseValue = Symbol("PromiseValue")//当前值
    return class {
   
   
        //构造器
        constructor(executor) {
   
   
            //...
        }
})()

我们都知道,当我们new Promise的时候,会有一个状态,会有值,还会传入两个函数,resolvereject。那么我们实现一下:

constructor(executor) {
   
   
    //初始化
    this[PromiseStatus] = PENDING //当前状态
    this[PromiseValue] = undefined //当前值
    /**
     * 定义 resolve 函数
     * @param {*} data: 要返回的数据
     */
    const resolve = (data) => {
   
   
        //...
    }
    /**
     * 定义reject函数
     * @param {*} data: 要返回的数据
     */
    const reject = (data) => {
   
   
        //...
    }
    //执行
    executor(resolve, reject)
}

接下来,我们就要实现resolvereject函数的功能,其实这两个函数的功能非常简单,就是修改当前的状态和值,并且如果有任务队列,我们就执行任务队列中的内容。我把这两个函数的功能封装一下:

/**
 * 改变状态的函数
 * @param {*} data: 数据
 * @param {*} status: 要改变的状态,resolve 或 reject
 * @param {*} queue: 任务队列
 */
[changeStatus](data, status, queue) {
   
   
    //如果已经是已决状态,那么直接结束
    if (this[PromiseStatus] !== PENDING) return;
    this[PromiseStatus] = status //修改当前状态
    this[PromiseValue] = data //修改值
    //变成已决阶段后,执行相应的队列函数
    queue.forEach(q => q(data))
}

//---- 在 resolve 函数中调用
this[changeStatus](data, RESOLVED, this[thenables])
//---- 在 reject 函数中调用
this[changeStatus](data, REJECTED, this[catchables])

接下来我们要实现thencatch这两个后续处理函数,这两个函数实现什么功能想必大家都知道了,但我们要注意的是,如果调用then或者catch的时候,当前的状态已经是已决状态,那么就要直接执行。如果不是,那么就加入任务队列中,我在构造器中已经声明了 thenablescatchables 变量(thencatch函数的任务队列)

 //settled then处理函数
then(thenable, catchable) {
   
   
    //每个then都要返回一个新的promise
    return this[linkPromise](thenable, catchable)
}
//settled catch处理函数
catch (catchable) {
   
   
    return this[linkPromise](undefined, catchable)
}

因为功能相同,我进行了 2 次封装,都是哪两次封装呢?这是第一次,也就是实现加入任务队列的功能:

/**
 * then和catch的处理函数,分为两种情况,如果当前已经是已决状态,
 * 那么直接执行(此时直接执行也要加入事件队列中,无法模拟微队列,只能用宏队列实现下),如果当前还是未决状态, 
 * 那么把当前的处理函数加入相应的任务队列中
 * @param {*} handler 处理函数
 * @param {*} queue   处理队列
 */
[settledHandler](handler, status, queue) {
   
   
    //如果不是函数,那么直接返回
    if (typeof handler !== "function") return
    if (this[PromiseStatus] === status) {
   
   
        //如果已经是已决状态,直接执行
        setTimeout(() => {
   
   
            handler(this[PromiseValue])
        }, 0);
    } else {
   
   
        //处于未决状态,加入任务队列
        queue.push(handler)
    }
}

还有一次封装,也就是我们上面调用的 linkPromise 函数,为什么要调用这个函数?因为thencatch后续处理函数,都要返回一个新的Promise,我们什么时候知道,then 或 catch 函数执行了,该返回新的Promise呢?这里是比较难的一部分,我们可以将 settledHandler 也就是执行处理函数的功能,反正创建一个新的 Promise中执行:

/**
 * 用于创建一个新的Promise, 当我们调用then和catch处理函数时, 会返回一个新的Promise
 * @param {*} thenable
 * @param {*} catchable 
 */
[linkPromise](thenable, catchable) {
   
   
    /**
     * 返回一个新的Promise的状态处理,如果父级已经变为已决状态, 那么新的Promise也是已决状态
     * @param {*} data 
     * @param {*} handler 
     * @param {*} resolve 
     * @param {*} reject 
     */
    function exec(data, handler, resolve, reject) {
   
   
        try {
   
   
            //获取返回值
            const res = handler(data)
            //如果返回的是一个Promise,此时我们直接处理一下就可以
            if (res instanceof MyPromise) {
   
   
                res.then(data => resolve(data), err => reject(err))
            } else {
   
   
                //改变状态,和修改值
                resolve(res)
            }
        } catch (error) {
   
   
            reject(error)
        }
    }
    //返回新的Promise
    return new MyPromise((r
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值