手写Promise 理解promise底层原理

本文深入探讨了Promise的工作原理,通过手写Promise实现,详细讲解了构造函数、resolve和reject、then、catch等核心方法的实现过程,帮助读者理解Promise的内部机制。同时介绍了静态方法如Promise.resolve()、Promise.reject()、Promise.all()和Promise.race()的用法,以及finally()的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文参考了此博客,在网上浏览了很多关于Promise的相关资料,promise可以说是JavaScript的重难点了,其中,很是感谢阮一峰老师的博客讲解以及MDN上的文档教程

如对Promise不太熟悉的同学,可以先浏览Promise工作原理分析,如有不同意见,欢迎讨论,一起进步。

Promise基本流程

function doSomething(){
   
    var p1 = new Promise(function (resolve, reject) {
   
        setTimeout(()=>{
   	//setTimeout1
            console.log('Start do something')
            resolve('Success!')
        },1000)
    })
    return p1
}
function successCallback(data){
   
    var p2= new Promise(function (resolve, reject) {
   
        setTimeout(()=>{
   	//setTimeout2
            console.log(data)
            resolve()
        },1000)
    })
    return p2
}
function failCallback(data){
   
    var p3 = new Promise(function (resolve, reject) {
   
        setTimeout(function () {
   
            console.log('Error1!')
            resolve()		//setTimeout3
        },1000)
    })
    return p3
}

//接下来去调用一下
doSomething()			//promise1
	.then(				
		(data)=>{
    		//promise2
    	return successCallback(data)},
		(data)=>{
   
    	return failCallback(data)}
    )
	.then(()=>{
   			//promise3
    return new Promise((resolve,rejected)=>{
   
        rejected('Unknown Error')
    })
})
	.catch(res=>{
   		//promise4
    console.log(res)
})

输出:

Start do something
Success!
Unknown Error

过程:

  • 我们来看一看这个promise链,首先执行了doSomething(),此方法返回了p1,并且在p1刚建立之时,此promise链就开始执行。
  • 当setTimeout1执行完之后(即setTimeout1中的resolve(‘Success!’)执行之后),p1的状态会由pending->fulfilled,然后调用then()方法,因为promise1已经fulfilled,所以,这里将调用then()方法中的第一个函数对象,在第一个函数对象中执行了successCallback(data)并返回了promise2。
  • 此时promise2的状态为pending,在setTimeout2中执行了 resolve()之后,其状态会由pending->fulfilled。
  • 然后再调用then()方法,这时候会生成promise3,其状态被设置为rejected,该异常会沿着promise链向下传递,最终被catch()捕获处理。

构造函数Promise必须接受一个函数对象作为参数,我们称其为handle,handle又包含了resolve和reject两个参数,这两个参数都是函数对象。

定义一个判断变量是否为函数的方法,后面会用到:

// 判断变量否为function
const isFunction = variable => typeof variable === 'function'

MyPromise实现promise功能

1. constructor

首先我们定义一个MyPromise的class,它接收一个函数handle作为参数

class MyPromise {
   
  constructor (handle) {
   
    if (!isFunction(handle)) {
   
      throw new Error('MyPromise must accept a function as a parameter')
    }
  }
}

我们从上方的过程中可以知道,promise主体执行异步程序,然后根据是否完成异步程序设置promise的状态,然后根据promise的状态调用then()方法。

所以,我们也为MyPromise添加一个状态标识

  • Pending(进行中)
  • Fulfilled(已成功)
  • Rejected(已失败)

状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。

2. resolve和reject

resolve和reject做了什么?

  • resolve : 将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)
  • reject : 将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败)
  • resolve 和 reject 都可以传入任意类型的值作为实参,表示 Promise 对象成功(Fulfilled)和失败(Rejected)的值
// 定义Promise的三种状态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

为MyPromise添加状态和值,并添加状态改变的执行逻辑:
注意: MyPromise的状态只能改变一次,即状态改变之前MyPromise的状态只能是PENDING

class MyPromise {
   
  constructor (handle) {
   
    if (!isFunction(handle)) {
   
      throw new Error('MyPromise must accept a function as a parameter')
    }
    // 添加状态
    this._status = PENDING
    // 添加状态
    this._value = undefined
    // 执行handle
    try {
   
      handle(this._resolve.bind(this), this._reject.bind(this)) 
    } catch (err) {
   
      this._reject(err)
    }
  }
  // 添加resovle时执行的函数
  _resolve (val) {
   
    if (this._status !== PENDING) return
    this._status = FULFILLED
    this._value = val
  }
  // 添加reject时执行的函数
  _reject (err) {
    
    if (this._status !== PENDING) return
    this._status = REJECTED
    this._value = err
  }
}

这里我们需要理解:

handle(this._resolve.bind(this),this._reject.bind(this)) 

我之前阅读这一句代码时陷入了思想误区,这里,我将详细展开为什么要这样写。

观察如下代码:

class myTest{
   
    constructor(handle) {
   
        this._sum = undefined
        handle(this._add.bind(this)) 
    }

    _add(a,b){
   
        this._sum = a + b
        return this._sum
    }

}

let test = new myTest((doSomething)=>{
   
    let [a, b] = [1, 2]
    console.log(doSomething(a,b))
})

在创建myTest时,传入了一个箭头函数:

(doSomething)=>{
   let [a, b] = [1, 2];console.log(doSomething(a,b))}

这个箭头函数就是myTest中的handle,然后调用了handle(this._add.bind(this)),这里向handle传入的参数为this._add函数对象,也就是说,这个箭头函数中的doSomething被赋值为了this._add。
平时我们常见到的参数赋值都是实例变量的赋值,而这里是函数变量的赋值,所以有一点难以理解,但是其实和实例变量赋值是一样的思想。
这里还涉及了this指向的知识点,bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。如有疑惑可以查看我的这一篇博客

3. then()

观察promise中的then()

我们已经知道promise的then()方法会接收两个函数对象作为参数,分别对应着promise状态为onFulfilledonRejected的回调函数

promise.then(onFulfilled, onRejected)
  • 如果onFulfilled或onRejected不是函数,其必须忽略。
  • then() 方法会返回一个新的promise对象,因此promise支持链式调用:
promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2);
  • 如果onFulfilled1onRejected1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值