手写Promise,让面试官看到不一样的答案,看完之后直接对promise毕业

2 篇文章 0 订阅
订阅专栏
本文详细介绍了Promise的基本概念、PromiseA+规范,以及作者自定义的Promise实现过程,包括状态管理、then方法的实现和错误处理。通过实例和面试题,帮助读者深入理解Promise的工作原理和用途。
摘要由CSDN通过智能技术生成

在网上看到了很多对promise的阐述手写promise,但是都不是很理想,于是决定自己写一篇

拒绝白嫖先赞后看养成习惯

有人想过Promise是干什么的吗,可能会有部分人觉得是把一个函数放到异步队列里面执行,但是看过这篇文章之后相信你会对他有更深入的见解

需要手写代码的同学可以直接拉到最后,如有看不懂的可以评论区留言,哪里有错误欢迎指正,或者是代码结构书写如果你有不同意见可以在评论区留言一起讨论

首先要搞清楚一个知识需要明确两个问题

  1. 它的背景是什么
  2. 它是怎么实现的

了解他是干什么的 为了解决什么问题 没有这个东西会影响什么 背景不在本章考虑范围之内,很多文章都有对它的阐述,也非常好

那么接下来就来阐述他是怎么实现的,以及promise是什么东西

Promise 按照 Promise A+ 规范实现,为了保证知识的准确性,我们这边将边看文档边写

Promise A+规范英文文档

Promise A+规范中文文档

描述Promise

直接看规范是怎么阐述的

image.png

3 4 5 暂时先不看

从这句可以得出两个点

  1. promise是一个对象或者是一个函数
  2. promise有一个then方法,并且这个then方法符合他的规范

我们这里实现promise使用对象的方式来实现Promise

这里首先看到术语的前两个,我用一行代码解释前两个表达的意思

image-20240204120827570.png

这里的Promise构造函数就是Promise 而这个构造函数返回的值就是thenable

也就是Promise构造函数里面定义了then函数那么这个构造函数就是Promise, 他返回的值就是thenable

从以上得知我们可以写一个构造函数MyPromise 构造函数里面有一个then 方法

image-20240207003353231.png

Promise的要求

image-20240204143605008.png

第一步描述了Promise的状态

三种状态,我们可以用三个常量表示

  • pending 待定
  • fulfilled 实现
  • rejected 拒绝

image-20240204144343449.png
状态是可以更改的,当是pending的时候可以变为fulfilled 或者是rejected

promise 这个待定就是表示对这个promise没有结果,当确定下来之后promise的状态只能是两种一个是fulfilled 另一个是rejected的,确定状态之后必须有一个值(undefined | null 也算是)

接下来就是实现状态和设置值

image-20240206212625102.png
我们这里参照v8的Promise,因为Promise只是描述你按照这个规范来写但是不管你怎么实现,你用函数实现也行用对象实现也行只要符合他的文档规范就行

官方的Promise是一个构造函数,需要你去new 并且有一个参数且为函数

image-20240206104750282.png

这里来看构造函数的三种情况

  1. 基础类型
  2. 对象
  3. 不传递
    image-20240206105310457.png

image-20240206175012915.png

image-20240206175150676.png

这里看到构造函数的参数只能是函数所以我们在*constructor*里面做一个判断使用new的时候必须传递一个函数

这个函数有两个参数

一个resolve 一个reject 这两个都是一个函数,第一个参数执行之后就会把这个这个promise的状态修改为成功,第二个则失败,里面可以传递一个参数作为成功之后的数据,失败之后的原因

image-20240206180210601.png
这个状态也只可以确定一次,当这个promise的状态更改过后则一切已经确定好了

image-20240206181202458.png

可以看到官方文档的这几句

大家看到下面这段话,不得更改,这个表示resolve(成功的值) | reject(失败原因) 传递过去的东西是不可以改变的,不可改变表示执行栈不可以改变,到时候我们把他单独存起来就行

大家看一下下面两张图片用来解释规范下面这段话

image-20240207101121816.png

image-20240207101318785.png

所以我们现在代码可以写为下面这种大家可以细看一下

image-20240206212053517.png

然后现在就考虑_resole 函数和_reject这两个函数

这个函数要做的事情也很简单

  1. 设置当前promise的状态
  2. 设置当前promise的值

我们这里调用一下之前写的函数就行

image-20240206211903845.png
调用executor过程中可能报错,官方的Promise是把当前的状态设置reject

这里我们简单用try catch包一下就行

image-20240206210808645.png

then

接下来就来实现then了,实现then我们要考虑一些问题参数,返回值

官方文档对then的描述,这里可能有点晦涩难懂,可以直接看下面我的描述

image-20240206212924730.png

从上面可以总结以下几点

  1. then的方法有两个参数,这两个参数的函数不能多次调用

  2. then方法必须返回一个promise

  3. 当promise状态确定下来之后才能调用这两个函数

    调用哪个,和怎么调用下文会详细说明这里暂时先不考虑

  4. 他的状态可以穿透

    如果不是一个函数则使用上一个的状态和值作为当前then返回的状态和数据

根据这两点我们来实现一下这个then

首先结构先搭建好

因为要进行链式调用所以这里我们可以再new 一次我们的Promise 这里他就返回一个pending的promise,这个promise携带then方法可以进行链式调用

image-20240206214411188.png

因为我们要等待状态确定好了之后才能执行这些方法,所以执行then的时候要把这些完成之后的函数,和失败之后的函数保存起来,等到状态确定下来的时候再从队列里面一个一个取出来进行操作(这里等待状态确定下来是指当前状态不为pending)所以我们这里维护一个队列

*constructor*里面定义一个处理队列

this._handles = []

在then执行的时候只需要把传递过来的函数添加到处理队列里面,因为我们要等状态确定了之后再判断要执行哪个函数,选择性执行,所以我们还要把当前的状态传递过去

以及设置当前的promise的状态,也就是then函数返回的这个promise,处理队列里面应该保存为一个对象

 {
    executor : Function,
    state : '状态',
    resolve,   //当executor执行成功了之后执行
    reject   // 当excutor执行失败了之后执行
}

这里有点绕,我们这里一个一个属性的详细解释

  1. executor,这个表示用户传递过来的数据,也就是then传递过来的参数,可选的,不传递就是undefined我们也把他放进去,或者是传递其他的值我们都把他放进去,因为等会要做状态穿透,只要参数不是一个函数,这个then函数返回的promise他的状态以及数据和上一个同步所以这里他传递什么东西都要给他放进去
  2. 这个state呢就表示我这个executor是成功之后执行还是失败之后执行,等状态确定下来了之后再做判断,筛选执行
  3. resolve和reject传递过去呢是为了设置在then函数里面new的我们自己的promise,因为现在还不能确定执行resolve还是执行reject等到将来处理队列的时候调用,executor没问题我们就执行resolve,他里面报错的话我们就执行reject

我们现在代码可以写为这种结构,下面是代码

image-20240206233427168.png

看到下面两句话

image-20240206234008982.png

image-20240206234039654.png

这里描述了执行then两个回调函数的过程需要把他放到异步,在浏览器可以通过 MutationObserver 在node 通过 process.nextTick

这里大家有兴趣可以点进去详细看一下,这里粗略过了直接上代码

大家可以把这段代码导出或者是直接写在这个文件里面

image-20240206235429276.png

下面是所有的代码

image-20240208181857748.png

下面代码用于复制,大家可以进行测试

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
​
const NODE = "node";
const BROWSER = "browser";
const OTHER = "other";
​
const env = (() => {
  if (typeof process === "object" && typeof process.nextTick === "function")
    return NODE;
  if (typeof MutationObserver === "function") {
    return BROWSER;
  }
  return OTHER;
})();
​
const microTask = {
  [NODE]: (callback) => {
    process.nextTick(callback);
  },
  [BROWSER]: (callback) => {
    const div = document.createElement("div");
    const observer = new MutationObserver(callback);
    observer.observe(div, {
      childList: true,
    });
    div.innerHTML = "1";
  },
  [OTHER]: (callback) => {
    setTimeout(callback, 0);
  },
};
​
/**
 * 添加一个函数到微队列里面
 * @param {Function} callback 任务函数
 */
function runMicroTask(callback) {
  microTask[env](callback);
}
​
/**
 * 判断函数是否为promise
 * @param {Object} obj
 */
function isPromise(obj) {
  return !!(obj && typeof obj === "object" && typeof obj.then === "function");
}
​
class MyPromise {
  /**
   *
   * @param {Function} executor 立即执行这个函数
   */
  constructor(executor) {
    if (typeof executor !== "function") {
      throw TypeError(`An argument is not a function`);
    }
    this._state = PENDING;
    this._value = undefined;
    this._handles = [];
    try {
      executor(this._resole.bind(this), this._reject.bind(this)); //这里需要使用bind绑定一下this,不然之后在resole里面使用this的话可能会出现问题造成参数访问不了,这个就不细讲了大家可以下去模拟一下
    } catch (exception) {
      this._reject(exception);
    }
  }
  /**
   * 设置promise的状态和值 状态和值只能修改一次
   * @param {String} state 状态
   * @param {any} value 值
   */
  _changeState(state, value) {
    if (this._state !== PENDING) return;
    this._state = state;
    this._value = value;
    this._runHandles()
  }
​
  /**
   * 取出处理队列里面的任务一个一个执行
   */
  _runHandles() {
    if (this._state === PENDING) return;
    while (this._handles[0]) {
      this._runOneHandles(this._handles[0]);
      this._handles.shift(); //每次执行完都要弹出去
    }
  }
  /**
   * 处理一项数据
   * @param {Object} param0
   */
  _runOneHandles({ executor, state, resolve, reject }) {
    runMicroTask(() => {
      if (state !== this._state) return;
      if (typeof executor !== "function") {
        state === FULFILLED ? resolve(this._value) : reject(this._value);
        return;
      }
      try {
        const resp = executor(this._value);
        if (isPromise(resp)) {
          resp.then(resolve, reject);
        } else {
          resolve(resp);
        }
      } catch (reason) {
        reject(reason);
      }
    });
  }
  /**
   * 设置当前promise的状态为成功 传递一个value为成功之后的数据
   * @param {any} value 成功的值
   */
  _resole(value) {
    this._changeState(FULFILLED, value);
  }
  /**
   * 调用这个函数设置promise的状态为失败 并且有一个失败的原因
   * @param {any} reason 失败的原因
   */
  _reject(reason) {
    this._changeState(REJECTED, reason);
  }
  /**
   * 向任务队列里面添加一项
   * @param {Function} executor 需要执行的函数
   * @param {String} state 当前这个函数是在什么状态下执行
   * @param {Function} resolve 设置当前promise为成功
   * @param {Function} reject 设置当前Promise为失败
   */
  _addOneHandles(executor, state, resolve, reject) {
    this._handles.push({
      executor,
      state,
      resolve,
      reject,
    });
  }
​
  /**
   * 可以多次调用
   * @param {Function} onFulfilled 完成之后执行的函数
   * @param {Function} onRejected 失败之后执行的函数
   */
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this._addOneHandles(onFulfilled, FULFILLED, resolve, reject);
      this._addOneHandles(onRejected, REJECTED, resolve, reject);
      this._runHandles()
    });
  }
}
​
​

里面有一些内部使用的方法,你可以在使用defineProperty控制一下

我们实现了简单的Promise,可以看到其实做的事情就是任务的调度,对一系列函数进行管控,当处于什么状态执行什么函数

面试题

这些题都可以使用上面我们自己手写的Promise进行分析

答案我这边不会给出,大家可以敲敲,验证一下结果,这些题做完可以说你对Promise已经有更深的见解了

image-20240207113545461.png

image-20240207113846588.png

image-20240207114722887.png

image-20240208230548384.png

麻烦点赞收藏加关注后续更新await到底在干什么

前端面试大全:手写 Promise
itKingOne的博客
01-15 2505
(内容同步自小邹的头条号:沪漂程序员的生活史) 在上一篇文章中我们了解了 Promise 的一些易错点,那这一篇文章,我们会通过手写一个符合 Promise/A+ 规范的 Promise 来深入理解它,并且手写 Promise 也是一道大厂常考题,在进入正题之前,推荐各位去网上找下Promise/A+规范,这样才能更好地理解这个章节的代码。   实现一个简易版 Promise 在完成符...
手把手一行一行代码教你“手写Promise“,完美通过 Promises/A+ 官方872个测试用例
圆圆的 JavaScript 博客
12-20 1752
保姆级教程,看完还不懂的私聊我亲自教!
手写Promise
XunLin233的博客
05-30 2003
在index.html中编写如下代码resolve("执行成功");})})创建一个promise.js文件在index.html中引入1. Promise能够通过new的方式调用,所以Promise是构造函数的形式。2. 构造Promise实例的时候传过去了一个函数,使用executor(执行器函数接收这个形参)。3. Promise实例能够调用then方法,所以在构造函数的原型上要添加then方法。4. then方法接收两个函数。// Promsie构造函数// 添加then方法。
超详细!全面搞懂手写Promise其过程
最新发布
书呆子ITme
08-13 1763
PromiseJavaScript中用于处理异步操作的一种方式。它提供了一种简洁且强大的方法来处理异步代码,避免了方法回调。本文将详细讲解如何从零开始手写一个Promise,帮助您深入理解其工作原理。PromisePending(待定):初始状态,既不是成功也不是失败。Fulfilled(已兑现):操作成功完成。Rejected(已拒绝):操作失败。通过手写Promise,我们深入了解了其内部工作原理。希望这篇技术文档对您理解Promise有所帮助。如果在实践中遇到问题,建议参考ES6标准中的。
手写promise-面试版
2402_85402030的博客
06-05 959
then回调是支持链式调用的,也就是then.then.then,并且由api特性可知then是return一个promise的,并且这个promise的res是上一个promise的。,仔细看这一步,我们之前的想法是让这个定时器回调push到队列中,但如果这样写的话相当于将回调执行后的返回值push到队列中,仔细想想是不是这样呢!的then回调,当then回调检测到状态没有发生改变则将回调函数push到各种的队列中,状态发生改变后在从队列中取出回调函数执行。时then回调会出现问题。
手写promise
GuoZebin的博客
05-31 420
前几天在看知乎的时候,无意间发现了一道面试题,手写promise,因为没研究过promise实现,所以对这个问题也是一脸懵逼,忍不住就研究了一下。 try { module.exports = Promise } catch (e) {} function Promise(executor) { var self = this self.status = 'pendi...
手写Promise
热门推荐
weixin_49487698的博客
05-16 1万+
一、JS的错误 1、错误的类型 2、错误处理 3、错误对象 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale
手写promise面试
01-08
手写Promise面试是一个通过手写代码来展示对Promise的理解和掌握程度的面试环节。在面试中,面试官往往会要求面试者用纸笔手写一个简单的Promise实现,或者是解答一些与Promise相关的问题。 首先,面试者需要熟悉...
看了就会,手写 Promise 全部 API 教程,包括处于 TC39 第四阶段草案的 Promise.any()
圆圆的 JavaScript 博客
12-21 578
根据规范实现Promise ES6+ 的全部 API,手写实现了 then 方法以外的两个实例方法以及目前 Promise 规范的全部六个静态方法。
一起来手写简易版promise
qq_43352105的博客
07-29 513
相信大家学了promise后,面试都会遇到手写promise的现象,我也尝试了通过自己的理解来简单的实现promise手写,发现只要将官方的promise整个API执行了解透彻,手写其实还是很好掌握的,一起来看看~ 核心逻辑 首先我们需要知道promise的核心逻辑 01 Promise是一个类,在执行类的时候,需要传递一个执行器,只要new了,执行器就会执行 02 Promise中有三种状态,等待Pending,成功Fulfilled,失败Rejectd,一旦确定就不能再更改 03 resolve和rej
手写一个promise
weixin_40821790的博客
07-24 687
class Qromise { constructor(fn) { this.status = 'pending' this.error = '' this.data = '' try{ fn(this.onFullfilled.bind(this), this.onRejected.bind(th...
手写实现promise
weixin_44935752的博客
08-22 271
function myPromise(constructor){ let self=this; self.status="pending" //定义状态改变前的初始状态 self.value=undefined;//定义状态为resolved的时候的状态 self.reason=undefined;//定义状态为rejected的时候的状态 function resolve(value){ //两个==="pending",保证了状态的改变是不可逆的
面试准备之手写Promise
weixin_49172439的博客
04-30 2418
本文是我在看完尚硅谷Promise教程后整理的手写Promsie,内容可能会稍许有点偏差,仅供参考。 当然,都看到手写Promise了,所以肯定对Promise的基本使用是没问题了,下面就来看看如何手写一份Promise吧。 一、搭建Promise骨架 新建lib文件夹,添加Promise.js文件。 首先采用ES5语法实现,后续会讲解ES6语法实现 // 自定义Promise函数模块: IIFE (function (window) { // Promise构造函数 // executor
面试题——————手写一个Promise
weixin_57089449的博客
03-21 1591
Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。一旦成功就不允许失败,一旦失败就不允许成功。 function Promise(excutor) { let self = this self.status = 'pending' self.value = null self.reason = null function resolve(value) { if (self.status ===
教你如何手写一个Promise
ose_huang的博客
02-25 9695
promise的进阶,手写一个自己的promise
手写promise,面试无敌
dear_mRZhou_的博客
06-24 1640
我们面试的时候经常会问到Promise的使用;有的面试官再深入一点,会继续问是否了解Promise实现方式,或者有没有阅读过Promise的源码;今天我们就来看一下,Promise在内部是如何实现来链式调用的。 首先我们定义一个promise的构造函数 在这里插入代码片 ...
写文章

热门文章

  • 手写Promise,让面试官看到不一样的答案,看完之后直接对promise毕业 1381
  • JavaScript你不知道的作用域 317

分类专栏

  • 前端 2篇

最新评论

  • 手写Promise,让面试官看到不一样的答案,看完之后直接对promise毕业

    CSDN-Ada助手: 恭喜你能够写出如此精彩的博客!看到你手写Promise并努力让面试官看到不一样的答案,我对你的努力和才华深感佩服。你的文章让我对Promise有了更深入的理解,直接想要对其进行进一步的学习和实践。 在下一步的创作中,我谦虚地建议你可以探索更多关于Promise的应用场景和实际案例。通过分享你在实际项目中使用Promise的经验,你可以让读者更好地理解Promise的实际应用价值,并且提供一些实用的技巧和建议。期待你能够继续创作出更多有价值的博客,让我们一起在学习中进步! CSDN 正在通过评论红包奖励优秀博客,请看红包流:https://bbs.csdn.net/?type=4&header=0&utm_source=csdn_ai_ada_blog_reply3

  • JavaScript你不知道的作用域

    CSDN-Ada助手: 非常棒的博文!你的标题引人入胜,而摘要中提到的作用域问题也非常具有吸引力。你的内容引发了读者的思考,让他们开始重新审视自己对作用域的理解。 除了你提到的预编译和变量提升之外,还有一些与作用域相关的扩展知识和技能。例如,你可以介绍闭包的概念和用法,以及如何使用匿名函数创建闭包。另外,你还可以讨论作用域链的概念和原理,以及如何在不同的作用域中访问变量。 继续保持创作,期待看到更多关于JavaScript作用域的精彩内容!同时也鼓励你在博文中加入更多实例和案例,以帮助读者更好地理解和应用这些概念。 如何写出更高质量的博客,请看该博主的分享:https://blog.csdn.net/lmy_520/article/details/128686434?utm_source=csdn_ai_ada_blog_reply2

最新文章

  • JavaScript你不知道的作用域
2024年1篇
2023年1篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

玻璃钢生产厂家温州学校玻璃钢雕塑价位北京玻璃钢招彩绘雕塑工仿真恐龙玻璃钢雕塑福建艺术商场美陈市场价有诚信的玻璃钢雕塑深圳节庆商场美陈哪里有校园玻璃钢雕塑批发洛阳抽象玻璃钢卡通雕塑厂家绍兴欧式玻璃钢雕塑哪家便宜鹤壁公园不锈钢玻璃钢雕塑厂家南昌玻璃钢马雕塑南关玻璃钢雕塑厂家河南玻璃钢雕塑摆件研究开封制作玻璃钢雕塑厂哪家好上海户外商场美陈市场价个性化玻璃钢雕塑批发大型商场美陈制造福州玻璃钢公园雕塑无锡宣城玻璃钢雕塑天津特色玻璃钢雕塑制作四川主题商场美陈市场价实惠的景观玻璃钢雕塑玻璃钢埃及雕塑供应商玻璃钢人物雕塑选哪家佛山关公财神玻璃钢雕塑南京特色玻璃钢花盆玻璃钢雕塑赋码玻璃钢校园文化雕塑定制牛玻璃钢雕塑图片虹口区专业玻璃钢雕塑服务至上香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化