从0实现一个基本的Promise类(一)

155 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

前言

“手写Promise”系列作为前端常考点,深度检查了应聘者对Promise使用以及规范的理解,忏愧的是本人也是最近在完全通关这一模块。

此篇作为复习笔记,以及学习总结。

手写Promise包含以下知识点 👇:

  • Promise基础知识
  • Class 类
  • 改变this指向 (call、apply和bind)
  • 事件循环 Event Loop

其中Promise基础知识如果不了解或有遗忘的同学可以参考我这篇文章:《简单明了的Promise基本知识点》

看完本篇你能学到:

  1. 了解Promise构造时的处理
  2. 了解Promise运行机制
  3. 了解实现一个简易的Promise

定义初始结构

原生的promise我们都通过new关键字来新建实例:

const usePromise = new Promise()

首先创建一个我们手写的Promise类,命名为myPromise

class myPromise{}

其次编写其的构造函数,我们知道Promise在实例化时要求传入一个类型为函数的参数,并且会在创建时就运行这个函数。

class myPromise{
    constructor(func){
        func()
    }
}

实现resolvereject

随后,实例化时传入的这个func应当传入俩个参数:resolvereject

class myPromise{
    constructor(func){
        func(resolve,reject)
    }
}

随即迎来下一个问题:resolve和reject我们尚未声明。于是我们应该当在类中显示声明 image-20220725193247381.png 创建这俩个方法后,编译器提示我们需要使用this关键字。原来,我们在类里面调用自身的属性时需要使用this关键字啊!

image-20220725193419602.png

加上后编译器就提示正确了,但是我们在resolvereject方法中需要干什么呢?

管理状态

仔细查看以下Promise流程图

Promise执行流程.png 我们发现resolve和reject都改变了promise的状态!但是我们好像并未赋予实例属性PromiseState,马上给他赋予下。并且声明三个静态属性:PENDING、FULFILLED、REJECTED,将PENDING作为默认值在初始化时赋予给状态。

class myPromise{
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';
    constructor(func){
        this.PromiseState =  myPromise.PENDING
        func(this.resolve,this.reject)
    }
    resolve(){
    
    }
    reject(){
     
    }
}

我们已知在原生的Promise中状态一经修改变无法修改,所以在resolve和reject中修改状态前要判断当前状态是否为初始状态pending。

class myPromise{
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';
    constructor(func){
        this.PromiseState =  myPromise.PENDING
        func(this.resolve,this.reject)
    }
    resolve(){
        if(this.PromiseState == myPromise.PENDING){
            this.PromiseState = myPromise.FULFILLED
        }
    }
    reject(){
        if (this.PromiseState == myPromise.PENDING){
            this.PromiseState = myPromise.REJECTED
        }
    }
}

管理结果

image-20220725091011845.png

如上图我们发现在原生的Promise中除了PromiseState外还有PromiseResult属性。而这个属性就是用来保存我们执行resolve和reject时,传递的参数。

let promise = new Promise((resolve, reject) => {
    resolve('执行成功')
})
console.log(promise)
/*Promise {<fulfilled>: '执行成功'}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "执行成功" */

因此,我们在我们的手写类中添加一个PromiseResult属性,并将初始值赋值为undefined,在执行resolve和reject时改变它。

class myPromise{
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';
    constructor(func){
        this.PromiseState =  myPromise.PENDING
        this.PromiseResult = undefined
        func(this.resolve,this.reject)
    }
    resolve(result){
        if(this.PromiseState == myPromise.PENDING){
            this.PromiseState = myPromise.FULFILLED
            this.PromiseResult = result
        }
    }
    reject(reason){
        if (this.PromiseState == myPromise.PENDING){
            this.PromiseState = myPromise.REJECTED
            this.PromiseResult = reason
        }
    }
}

this 指向问题

以上代码我们好像已经实现了一个拥有状态并能改变状态的promise类了,现在我们新建一个实例来执行下代码。

let promiseTest = new myPromise((resolve,reject)=>{
    resolve('changeStateToFulfullied')
})

运行上面代码,报错 🦁:

Uncaught TypeError: Cannot read property 'PromiseState ' of undefined

原因是我们在运行resolve以及reject时,函数的this指针已经从实例类变成了运行时函数环境也就是:

{
     resolve('changeStateToFulfullied')
}

因此我们需要在初始化时,固定下这俩个函数的this指针。即:

constructor(func){
    this.PromiseState =  myPromise.PENDING
    this.PromiseResult = undefined
    func(this.resolve.bind(this),this.reject.bind(this))
}

在resolve中加上console.log(result),并再次运行😎

执行成功

这样我们就实现了一个与原生Promise具有相同属性PromiseStatePromiseResult并能够改变属性的类

Next

由于将整个Promise细节堆在一起讲解篇幅会过长,我将文章也拆分为了五个小段进行描述。目的是为了让大家能够理解到不同的阶段,能够充分的消化并在实战中使用。

下一期从0实现一个基本的Promise类(二)会讲述

  • 实现then方法
  • 实现异步