JavaScript实现Promise

devtools/2024/11/19 15:17:19/

第一步:编写constructor构造方法

javascript">const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';class MyPromise {#state = PENDING;#result = undefined;constructor(executor) {const resolve = (data) => {this.#changeState(FULFILLED, data)}const reject = (reason) => {this.#changeState(REJECTED, reason)}executor(resolve, reject)}#changeState(state, result) {if (this.#state === PENDING) return; // 状态一旦定下来,就不能再改变了this.#state = state;this.#result = result;}
}const p = new MyPromise((resolve, reject) => {resolve(1);reject(123); // 这里调用,不会起到任何作用
})console.log(p);

注意:状态一旦确定,就不能再改变的promise的状态

第二步:考虑异常情况

javascript">//用try catch包裹executor方法处理
//...try {executor(resolve, reject)} catch (err) {reject(err)}
//...const p = new MyPromise((resolve, reject) => {throw 123;
})

但是这种处理方式,对于异步的报错,没有办法

javascript">const p = new MyPromise((resolve, reject) => {setTimeout(() => {throw 123;}, 1000);
})

这个异步抛异常的问题是没法解决的,官方的实现也存在这个问题,异步抛异常是无法改变promise状态(没有执行changeState方法)

第三步:实现then方法(比较复杂)

一般会这么调用then方法

javascript">const p = new MyPromise((resolve, reject) => {setTimeout(() => {resolve("ok");}, 1000);
})//这个方法会优先于上面的setTimeout方法执行,所以会被记录,然后在setTimeout后执行
p.then(res => {console.log(res + 1);},err => {console.log(err);}
)p.then(res => {console.log(res + 2);},err => {console.log(err);}
)

图解:在调用then方法的时候,记录当前的参数 ,等setTimeout时间一到,就去执行#run方法
在这里插入图片描述

此时实现的MyPromise的代码如下

javascript">const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';class MyPromise {#state = PENDING;#result = undefined;#handlers = []; //记录多少个then的回调方法constructor(executor) {const resolve = (data) => {this.#changeState(FULFILLED, data)}const reject = (reason) => {this.#changeState(REJECTED, reason)}try {executor(resolve, reject)} catch (err) {reject(err)}}//状态改变时,调用这个方法#changeState(state, result) {if (this.#state !== PENDING) return; // 状态一旦定下来,就不能再改变了this.#state = state;this.#result = result;this.#run();}//执行handlers里面的方法,handlers的方法是调用then时,所记录的方法#run() {if (this.#state === PENDING) return;while (this.#handlers.length) {const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift();if (this.#state === FULFILLED) {if (typeof onFulfilled === 'function') {onFulfilled(this.#result);}} else {if (typeof onRejected === 'function') {onRejected(this.#result);}}}}then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) => {//记录到#handlers这个变量中,等setTimeout时间一到,就去执行onFulfilled或者onRejected方法//resolve和reject是返回的一个新的promise的参数,这个新的promise用户链式调用(下面会写)this.#handlers.push({onFulfilled,onRejected,resolve,reject})this.#run();})}
}const p = new MyPromise((resolve, reject) => {setTimeout(() => {resolve("ok");}, 1000);
})//这个方法会优先于上面的setTimeout方法执行,所以会被记录到setTimeout后执行
p.then(res => {console.log(res + 1);},err => {console.log(err);}
)p.then(res => {console.log(res + 2);},err => {console.log(err);}
)

第四步:then的链式调用(比较复杂)

返回的是一个新的promise对象(以前面试有被问道,我回答错了)

javascript">//1.编写constructor构造方法,需要考虑状态一旦确定,就不能再改变的因素
const p = new MyPromise((resolve, reject) => {setTimeout(() => {resolve(1);}, 1000);
})//这个方法会优先于上面的setTimeout方法执行,所以会被记录到setTimeout后执行
p.then(res => {console.log(res); //1return res + 1},err => {console.log(err);}
).then(res => {console.log(res); //2
}, err => {console.log(err);
})
javascript">// 手写Promiseconst PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';class MyPromise {#state = PENDING;#result = undefined;#handlers = [];constructor(executor) {const resolve = (data) => {this.#changeState(FULFILLED, data)}const reject = (reason) => {this.#changeState(REJECTED, reason)}try {executor(resolve, reject)} catch (err) {reject(err)}}#changeState(state, result) {if (this.#state !== PENDING) return; // 状态一旦定下来,就不能再改变了this.#state = state;this.#result = result;this.#run();}//判断是不是Promise类型#isPromiseLike(value) {if (value !== null && (typeof value === 'object' || typeof value.then === 'function')) {return typeof value.then === 'function';}return false;}//微队列#runMicroTask(func) {//node环境中if (typeof process === 'object' && typeof process.nextTick === 'function') {process.nextTick(func);}//浏览器环境中else if (typeof MutationObserver === 'function') {const observer = new MutationObserver(func); //观察器const p = document.createTextNode('1');observer.observe(p, { characterData: true }); //当p节点发生改变的时候,执行funcp.data = '2';} else {setTimeout(func, 0);}}#runOne(callback, resolve, reject) {this.#runMicroTask(() => {//如果传来的不是function类型if (typeof callback !== 'function') {const settled = this.#state === FULFILLED ? resolve : reject;settled(this.#result); //将把当前的promise状态,传给下一个promisereturn;}//如果传来的是function类型try {const data = callback(this.#result);if (this.#isPromiseLike(data)) {data.then(resolve, reject);} else {reject(data);}} catch (err) {reject(err)}})}#run() {if (this.#state === PENDING) return;while (this.#handlers.length) {const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift();if (this.#state === FULFILLED) {this.#runOne(onFulfilled, resolve, reject)} else {this.#runOne(onRejected, resolve, reject)}}}then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) => {this.#handlers.push({onFulfilled,onRejected,resolve,reject})this.#run();})}
}//1.编写constructor构造方法,需要考虑状态一旦确定,就不能再改变的因素
const p = new MyPromise((resolve, reject) => {setTimeout(() => {resolve(1);}, 1000);
})//这个方法会优先于上面的setTimeout方法执行,所以会被记录到setTimeout的定时时间到后执行
p.then(null, //如果不是一个函数,就延续上一个promise的状态err => {console.log(err);}).then(data => {console.log(data); //1},err => {console.log(err);})

http://www.ppmy.cn/devtools/135228.html

相关文章

鸿蒙 管理应用拥有的状态有Localstorage、Appstorage、PersistentStorage、Environment、用户首选项、持久化方案。

LocalStorage: LocalStorage是页面级UI状态存储,通过Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例。支持UIAbility实例内多个页面间状态共享。 // 存储数据 localStorage.setItem(key, value); // 获取数据 const value localStorage…

django从入门到实战(四)——模型与数据库

1. 模型的定义与数据迁移 1.1 模型的定义 在 Django 中,模型是一个 Python 类,用于定义数据库中的数据结构。每个模型类对应数据库中的一张表,类的属性对应表中的字段。 示例: from django.db import modelsclass Blog(models…

Vue.js 前端框架入门

简介 Vue.js 是一个构建用户界面的渐进式JavaScript框架。本文将带你了解Vue项目的目录结构,启动顺序,并逐步指导你安装必要的环境,以及如何开发一个基础的Vue项目。 需要的环境 Node.js:Vue.js 项目依赖于Node.js,…

网络基础(3)https和加密

http其它的报头 直接看图片: 上图中的第一个和第二个类型之前已经使用过了也就不多做说明了,第三个报头类型使用的很少了。第四个报头类型主要就使用在一些灰度更新的应用上,确定用户使用的软件的版本不让其访问该版本不能访问的功能。下一个…

Vue3 provide 和 inject的使用

在 Vue 中,provide 和 inject 是 Composition API 的一对功能,用于父子组件之间的依赖注入。它们的作用是让父组件可以向其所有子组件提供数据或方法,而不需要通过逐层传递 props。 1. provide provide 用于父组件中,提供数据或…

redis的击穿和雪崩

Redis 是一个高性能的键值存储数据库,广泛用于缓存、会话管理等场景。然而,Redis 在高并发场景下可能会遇到一些问题,比如“击穿”和“雪崩”。下面详细解释这两个概念: 击穿(Hotspot) 击穿是指某个热点数…

Springboot 整合 Java DL4J 打造金融风险评估系统

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,高并发设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s…

shell 接收长参数

以备份 mysql 脚本为例,如下 #!/bin/bash # 分隔符 str"-"# 定义分割线函数 FenGeLine() {## 获取屏幕宽度ScreenLen$(stty size |awk {print $2})## 标题宽度TitleLen$(echo -n $1 |wc -c)#echo $TitleLen## 左右分割线长度LineLen$(((${ScreenLen} - $…