300秒带你手写一个promise

news2025/7/5 14:29:18

前言

为什么要写这一篇文章?

  • 加深对promise的理解,以及再实际工作中的灵活运用。
  • 知其然,知其所以然。
  • 面试需要。(重点!!!)

1:声明promise

首先我们先来聊聊promise的几个特性:

  • 一个promise有三种状态:pengding,fulfilled,rejected.
  • promise状态一旦被改变,就不允许被改变了。
  • new Promise构造函数接受一个函数参数,这个函数有两个参数分别为resole和reject函数。

其实代码很好写,我们就来检验以下resolve的参数是否能正确传递吧

const PENDING = "pengding";
const FUFILLED = "fulfilled";
const REJECTED = "rejected";
class MP {
    constructor(executor) {
        this.status = PENDING;
        this.value = null;
        executor(this.resolve, this.reject);
    }
    resolve(value) {
        this.value = value;
        console.log("resolve:" + this.value);
    }
    reject(season) {}
}
// test
new MP((resolve, reject) => {
    resolve("你好");
}); 

打开控制台,我们发现无法设置属性value,

image.png 查看代码我们很容易发现其实代码this绑定的问题,我们用bind绑定以下就好了。

image.png

image.png this问题解决了,但是还有一个问题,状态只能改变一次。所以我们在resolve和reject函数执行的时候要判断一下状态。

const PENDING = "pengding";
const FUFILLED = "fulfilled";
const REJECTED = "rejected";
class MP {
    constructor(executor) {
        this.status = PENDING;
        this.value = null;
        executor(this.resolve.bind(this), this.reject.bind(this));
    }
    resolve(value) {
        if(this.status == PENDING){
            this.status = FUFILLED;
            this.value = value;
            console.log("resolve:" + this.value);
        }
        
    }
    reject(season) {
        if (this.status == PENDING) {
            this.status = REJECTED;
            this.value = season;
            console.log("reject:" + this.value);
        }
    }
}
// test
new MP((resolve, reject) => {
    resolve("你好");
}); 

2:then的基础构建

2.1:then的用法

  • 接受两个参数,当promise成功执行第一个函数参数,失败执行第二个函数参数。
  • then里面也可以不传参数。
  • then里面的函数为异步操作。(promise是微任务)
then(onFulfilled, onRejected) {
        // 判断两个参数是不是函数类型,如果不是函数,需要手动封装,否则下面执行时会报错。
        if (typeof onFulfilled !== "function") {
            onFulfilled = () => {};
        } else if (typeof onRejected !== "function") {
            onRejected = () => {};
        }
        if (this.status === "fulfilled") {
            onFulfilled(this.value);
        } else if (this.status === "rejected") {
            onRejected(this.value);
        } else {
        }
    }
 
 

// test
new MP((resolve, reject) => {
    resolve("你好");
}).then(
    res => {
        console.log("then res", res);
    },
    rej => {
        console.log(rej);
    }
); 

image.png then可以接收到resolve传过来的数据。

2.2:异步操作(任务队列不懂的先去学学)

这里我们使用queueMicrotask来创建微任务:

then(onFulfilled, onRejected) {
        // 判断两个参数是不是函数类型,如果不是函数,需要手动封装,否则下面执行时会报错。
        if (typeof onFulfilled !== "function") {
            onFulfilled = () => {};
        } else if (typeof onRejected !== "function") {
            onRejected = () => {};
        }
        if (this.status === "fulfilled") {
            queueMicrotask(()=>{
                onFulfilled(this.value);
            });
            
        } else if (this.status === "rejected") {
            queueMicrotask(()=>{
                onRejected(this.value);
            });
            ;
        } else {
        }
    }
}

// test
new MP((resolve, reject) => {
    resolve("中国");
    console.log('你好');
}).then(res=>{
    console.log(res)
}); 

image.png

顺序是正确的,同步任务打印的你好优先于异步任务打印的中国。

2.3:then处理pengding状态

当我们在执行then方式的时候,状态还是没有改变的情况。(什么情况状态没有改变:异步执行resolve或者reject的时候 )之前的代码只判断了fulfilled和rejected的状态。

// test
new MP((resolve, reject) => {
    setTimeout(() => {
         resolve("异步操作"); 
    },1000);
}).then(res=>{
    console.log(res);
}); 

这个时候控制台是不答应任何东西的,因为还没有处理这个状态。所以说我们之前的思路就有一点问题,在then函数中我们不应该控制then里面的两个参数函数的执行,应该去保存它,在resolvereject方式再去执行。

const PENDING = "pengding";
const FUFILLED = "fulfilled";
const REJECTED = "rejected";
class MP {
    constructor(executor) {
        this.status = PENDING;
        this.value = null;
        // 将回调函数保存起来
        this.callbacks = [];
        executor(this.resolve.bind(this), this.reject.bind(this));
    }
    resolve(value) {
        if (this.status == PENDING) {
            let that = this;
            this.status = FUFILLED;
            this.value = value;
            //  // 触发回调函数
            queueMicrotask(() => {
                that.callbacks.map(callback => {
                    callback.onFulfilled(that.value);
                });
            });
        }
    }
    reject(season) {
        if (this.status == PENDING) {
            let that = this;
            this.status = REJECTED;
            this.value = season;
            // 触发回调函数
            queueMicrotask(() => {
                that.callbacks.map(callback => {
                    callback.onRejected(that.value);
                });
            });
        }
    }
    then(onFulfilled, onRejected) {
        // 判断两个参数是不是函数类型,如果不是函数,需要手动封装,否则下面执行时会报错。
        if (typeof onFulfilled !== "function") {
            onFulfilled = () => {};
        } else if (typeof onRejected !== "function") {
            onRejected = () => {};
        }
        if (this.status === "fulfilled") {
            // 收集回调函数
            this.callbacks.push({
                onFulfilled
            });
        } else if (this.status === "rejected") {
            // 收集回调函数
            this.callbacks.push({
                onRejected
            });
        } else {
            // 收集回调函数
            this.callbacks.push({
                onFulfilled,
                onRejected
            });
        }
    }
} 

2.4:then链式调用

  • then返回的也是一个promise
  • 之前promise的状态不会影响新的promise状态。

所以我们现在要继续处理我们的then方法,首先在then函数里肯定要返回一个promise的,其次在then函数里面我们要拿到onFulfilled, onRejected这两个回调函数的结果,作为这个新的promise的值。

then(onFulfilled, onRejected) {
        // 判断两个参数是不是函数类型,如果不是函数,需要手动封装,否则下面执行时会报错。
        if (typeof onFulfilled !== "function") {
            onFulfilled = () => {};
        } else if (typeof onRejected !== "function") {
            onRejected = () => {};
        }
        // 直接返回一个新的promise 之前在then里面处理的代码迁移到新的promise里面处理
        return new MP((resolve,reject)=>{
            // 这时里面的this指向的不是新的promise
            if (this.status === "fulfilled") {
                this.callbacks.push({
                    // 这个改成箭头函数的形式,方便我们获取onFulfilled的值传递给新promise的resolve
                    onFulfilled:value=>{
                        let result = onFulfilled(value)
                        resolve(result)
                    }
                });
            } else if (this.status === "rejected") {
                this.callbacks.push({
                    onRejected:value=>{
                        let result = onRejected(value);
                        resolve(result);
                    }
                });
            } else {
                this.callbacks.push({
                    onFulfilled: value => {
                        onFulfilled(value);
                    },
                    onRejected: value => {
                        onRejected(value);
                    }
                });
            }
        })
    }
    
// test
new MP((resolve, reject) => {
    resolve("异步操作");
}).then(
    res => {
        return '33333';
    },
    rej => {return '11111111'}
).then(res=>{
    console.log(res)
}); 

在第二个then打印的结果应该是第一个then中返回的33333

image.png

2.5:then回调函数返回promise

new Promise((res,rej)=>{
   res('1111')
}).then(res=>{
    return new Promise((res,rej)=>{
        res('22222')
    })
}).then(res=>{
    console.log(res)
}); 

打印结果应该为22222而不是promise对象。

image.png 用我们手写的试试

new MP((resolve, reject) => {
    resolve("异步操作");
}).then(
   res=>{
       return new MP((res, rej) => {
           res("22222");
       });
   }
).then(res=>{
    console.log(res)
}); 

image.png 返回的却是一个promise对象,这是因为我们在处理回调函数结果时,没有判断类型,直接将整个result都返回给下个then了,所以我们在返回之前,需要判断以下返回的结果。

 then(onFulfilled, onRejected) {
        // 判断两个参数是不是函数类型,如果不是函数,需要手动封装,否则下面执行时会报错。
        if (typeof onFulfilled !== "function") {
            onFulfilled = () => {};
        } else if (typeof onRejected !== "function") {
            onRejected = () => {};
        }
        return new MP((resolve,reject)=>{
            if (this.status === "fulfilled") {
                this.callbacks.push({
                    onFulfilled:value=>{
                        let result = onFulfilled(value);
                        // 结果时MP类型时
                        if(result instanceof MP){
                            // 我们可以直接通过这个promise的then方法来传递值
                            result.then(resolve, reject);
                        }else{
                            resolve(result);
                        }
                    }
                });
            } else if (this.status === "rejected") {
                this.callbacks.push({
                    onRejected:value=>{
                        let result = onRejected(value);
                        // 结果时MP类型时
                        if (result instanceof MP) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    }
                });
            } else {
                this.callbacks.push({
                    onFulfilled: value => {
                        let result = onFulfilled(value);
                        // 结果时MP类型时
                        if (result instanceof MP) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    },
                    onRejected: value => {
                        let result = onRejected(value);
                        // 结果时MP类型时
                        if (result instanceof MP) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    }
                });
            }
        })
    } 

打开控制台就是具体的值而不是promise.

image.png

2.6:prmise返回类型约束

看一段代码

let promise = new Promise((res,rej)=>{
    res('111')
})
let p = promise.then(res=>{
    return p
}) 

image.png 我们发现控制台报错了,说明promise不能返回自己,对于返回类型是有约束的.

then(onFulfilled, onRejected) {
        // 判断两个参数是不是函数类型,如果不是函数,需要手动封装,否则下面执行时会报错。
        if (typeof onFulfilled !== "function") {
            onFulfilled = () => {};
        } else if (typeof onRejected !== "function") {
            onRejected = () => {};
        }
        let promise =  new MP((resolve,reject)=>{
            if (this.status === "fulfilled") {
                this.callbacks.push({
                    onFulfilled:value=>{
                        let result = onFulfilled(value);
                        if(result  === promise){
                            throw new TypeError(
                                "Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>"
                            );
                        }
                        // 结果时MP类型时
                        if(result instanceof MP){
                            result.then(resolve, reject);
                        }else{
                            resolve(result);
                        }
                    }
                });
            } else if (this.status === "rejected") {
                this.callbacks.push({
                    onRejected:value=>{
                        let result = onRejected(value);
                        // 判断返回类型
                        if (result === promise) {
                            throw new TypeError(
                                "Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>"
                            );
                        }
                        // 结果时MP类型时
                        if (result instanceof MP) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    }
                });
            } else {
                this.callbacks.push({
                    onFulfilled: value => {
                        let result = onFulfilled(value);
                        if (result === promise) {
                            throw new TypeError(
                                "Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>"
                            );
                        }
                        // 结果时MP类型时
                        if (result instanceof MP) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    },
                    onRejected: value => {
                        let result = onRejected(value);
                        if (result === promise) {
                            throw new TypeError(
                                "Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>"
                            );
                        }
                        // 结果时MP类型时
                        if (result instanceof MP) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    }
                });
            }
        })
        return promise
    } 

3:实现Promise.resolve和Promise.reject

例子:

Promise.resolve('111').then(res=>{
    console.log(res)
})
Promise.reject('111').then(res=>{
    console.log(res)
},rej=>{
    console.log(rej)
}) 

其实这个方法就是帮助我们做了两件事情

  • 创建一个promise
  • 将promise的状态设置成功或者失败
static resolve(value){
        return new MP(resolve => {
            if (value instanceof MP) {
                value.then(resolve);
            } else {
                resolve(value);
            }
        });
    } 

reject同理就不用写了哈。

4:实现Promise.all

Promise.all方法会返回一个promise,当参数数组中所有promise都成功后,就会返回成功的promise.看到所有时,我们就需要一个计数器,每次成功一个记录一次,当记录结果和传入数组长度一样时,就返回成功的,当一个失败时,就直接返回失败的。

static all(promises){
        let length = promises.length;
        // 记录每个promise成功时结果,同时用于返回
        let arr = [];
        return new MP((resolve, reject) => {
            for (let i = 0; i < length; i++) {
                promises[i].then(
                    res => {
                        arr[i] = res;
                        if (arr.length === length) {
                            resolve(arr);
                        }
                    },
                    rej => {
                        reject(rej);
                    }
                );
            }
        });
    } 

结尾

喜欢的小伙伴可以点赞收藏哈,有没有一起备战面试的,大家一起加油哈~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/407392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【node.js从入门到精通】使用node.js写入读取文件内容,基础方法使用

目录 前言 读取文件内容 判断文件是否读取成功 使用writeFile写入文件内容 整理数据 路径动态拼接处理问题 path.basename使用 获取路径中扩展名文件 写在最后 前言 Node.js是一个基于Chrome V8引擎的JavaScript运行环境。 区分版本号 LTS为长期稳定版&#xff0c;推…

大数据 │ ECharts与pyecharts数据可视化应用

通过本篇了解ECharts与pyecharts数据可视化的特点&#xff0c;能实现简单的ECharts与pyecharts数据可视化操作。 01、pyecharts数据可视化介绍 pyecharts是一个用于生成 Echarts 图表的类库&#xff0c;是一款将Python与ECharts相结合的强大的数据可视化工具&#xff0c;使用…

npm install报错Fix the upstream dependency conflict, or retry

执行 npm install vue-router时报错&#xff0c;进过多方查找&#xff0c;最终逐渐了解到造成此问题的原因。 从报错的信息&#xff1a; ERESOLVE unable to resolve dependency tree &#xff08;无法解决依赖关系树&#xff09; npm ERR! Could not resolve dependency: &…

9、Linux 高并发Web服务器项目实战(附代码下载地址)

1. Linux / Unix 上的五种 IO 模型 在Linux下进行网络编程时&#xff0c;服务器端编程经常需要构造高性能的IO模型&#xff0c;常见的IO模型有五种&#xff1a; 同步阻塞式 I/O&#xff08;BIO, Blocking IO&#xff09;&#xff1a; 在调用该类I/O函数读取数据时&#xff0c;…

前端LayUI框架快速上手详解(一)

✍目录总览 &#x1f525;LayUI &#x1f525;前端框架LayUI详解地址&#x1f525;前端LayUI框架快速上手详解(一)https://blog.csdn.net/Augenstern_QXL/article/details/119748962&#x1f525;前端LayUI框架快速上手详解(二)https://blog.csdn.net/Augenstern_QXL/article/…

【JavaEE初阶】前端第一节.HTML 基础(上篇)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、前端的引入&#xff1b; 二、第一个HTML程序 1.1 如何创建一个新HTML文件 2.2 HTML的完整结构 2.3 关于html文件的详解 2.4 使用 开发者工具 三…

uniapp h5的支付宝,微信支付

根据官方文档可知&#xff0c;uniapp对h5平台的支付没有进行封装&#xff0c;这就需要我们的自己针对不同的情况进行处理。 uniapp官方支付文档&#xff1a;uni.requestPayment(OBJECT) | uni-app官网 前端在进行h5页面的支付处理的时还是较为简单的&#xff0c;不论是支付宝还…

大二C#实现酒店管理系统(C端展示、前台操作、登记入住、入住管理、职位管理、公告系统等)

大二C#实现酒店管理系统&#xff08;C端展示、前台操作、登记入住、入住管理、职位管理、公告系统等&#xff09; 前言项目技术介绍 1、开发工具&#xff1a;VS2022 2、数据库&#xff1a;SqlServer 3、前端请求&#xff1a;HTML5jQuery 4、第三方框架&#xff1a;Layui、视图可…

无界微前端使用方法

1、概念 微前端&#xff1a;字面意思就是小小的前端&#xff0c;怎么小就是不管什么项目都能放在一个容器中运行&#xff0c;这些项目都能够独立部署、独立扩展&#xff0c;每个服务都具有稳固的模块边界&#xff0c;甚至允许使用不同的编程语言来编写不同服务&#xff0c;也可…

Android ImageView使用详解(系列教程三)

目录 一、ImageView简介 二、ImageView基本使用 三、ImageView常用属性 四、几种图片的加载方法 五、ImageView的缩放类型 一、ImageView简介 ImageView是Android开发中最常用的组件之一&#xff0c;主要用于显示图片&#xff0c;但是它不只是能显示图片&#xff0c;任何D…

SSE:后端向前端发送消息(springboot SseEmitter)

背景 有一个项目&#xff0c;前端vue&#xff0c;后端springboot。现在需要做一个功能&#xff1a;用户在使用系统的时候&#xff0c;管理员发布公告&#xff0c;则使用系统的用户可以看到该公告。 基于此&#xff0c;一个简单的方案&#xff1a;前端使用JS方法setInterval&am…

二次封装 el-table

很多中后台业务的系统中&#xff0c;表格是最高频的组件之一&#xff0c;其中一般包括搜索条件、表格展示、表格操作列、分页等。那么我们二次封装的这个表格组件就需要包含以下几个功能点&#xff1a; 1、数据自动获取和刷新 2、自定义列配置 3、分页功能 4、根据搜索条件…

VUE-鼠标悬浮到目标区域变成小手模样

需求&#xff1a;在这个按钮上&#xff0c;当鼠标悬浮上时要变成小手。记录下自己的开发内容实现&#xff1a;就是针对CSS给这个样式指定个属性就好了【cursor: pointer;】单独加到你要实现需求的div内或者元素里也可以加在class中&#xff0c;定义一个class样式&#xff0c;最…

web数据可视化(ECharts版)

实训 实训1会员基本信息及消费能力对比分析 1&#xff0e;训练要点(1&#xff09;掌握堆积柱状图的绘制。(2&#xff09;掌握标准条形图的绘制。 (3&#xff09;掌握瀑布图的绘制。 2&#xff0e;需求说明 “会员信息表&#xff0e; xlsx ”文件记录了某鲜花店销售系统上的会员…

微信小程序 初学——【音乐播放器】

一、项目效果展示 音乐推荐页面展示 播放器展示 播放列表展示 二、项目结构 音乐小程序项目页面结构&#xff1a; 1.tab导航栏 2.content内容区 3.player音乐播放器控件开发者工具创建空白项目&#xff1a; 新建项目 —— 设置项目名称和路径 —— 选择你注册的AppID…

44岁了,我从没想过在CSDN创作2年,会有这么大收获

1998年上的大学&#xff0c;02年毕业&#xff0c;就算从工作算起&#xff0c;我也有20余年的码龄生涯了。 但正式开启博文的写作&#xff0c;却是2021年开始的&#xff0c;差不多也就写了2年的博客&#xff0c;今天我来说说我在CSDN的感受和收获。 我是真的没想到&#xff0c;…

和ChatGPT对比,文心一言的表现已经是中国之光了

网络上各种测评满天飞&#xff0c;这里就不展开说了&#xff0c;针对“chatgpt”这项技术的难点&#xff0c;是十分巨大的。当你对文心一言以及其他国产AI软件存在不满的时候&#xff0c;你可以简单对着chatgpt或者文心一言搜索&#xff01;ChatGPT技术难点通俗来讲难度&#x…

CSS实现文字凹凸效果

使用两个div分别用来实现凹凸效果&#xff1b;text-shadow语法 text-shadow: h-shadow v-shadow blur color; h-shadow&#xff1a;必需。水平阴影的位置。允许负值。 v-shadow &#xff1a;必需。垂直阴影的位置。允许负值。 blur&#xff1a;可选&#xff0c;模糊的距离。 co…

用chatgpt写insar地质灾害的论文,重复率只有1.8%,chatgpt4.0写论文不是梦

突发奇想&#xff0c;想用chatgpt写一篇论文&#xff0c;并看看查重率&#xff0c;结果很惊艳&#xff0c;说明是确实可行的&#xff0c;请看下图。 下面是完整的文字内容。 InSAR (Interferometric Synthetic Aperture Radar) 地质灾害监测技术是一种基于合成孔径雷达…

【小程序从0到1】小程序常用组件一览

欢迎来到我的博客 &#x1f4d4;博主是一名大学在读本科生&#xff0c;主要学习方向是前端。 &#x1f36d;目前已经更新了【Vue】、【React–从基础到实战】、【TypeScript】等等系列专栏 &#x1f6e0;目前正在学习的是&#x1f525;React/小程序React/小程序React/小程序&am…