Promise原理分析一

news/2024/7/16 22:54:39

Promise原理分析一

Promise对象用于异步计算。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。

Note:

  • Promise原理分析二

Promise对象有以下几种状态:

  • pending: 初始状态, 既不是 fulfilled 也不是 rejected.

  • fulfilled: 成功的操作.

  • rejected: 失败的操作.

pending状态的Promise对象既可转换为带着一个成功值的fulfilled状态,也可变为带着一个失败信息的 rejected状态。当状态发生转换时,Promise.then绑定的方法就会被调用。(当绑定方法时,如果 Promise对象已经处于fulfilled或rejected状态,那么相应的方法将会被立刻调用,所以在异步操作的完成情况和它的绑定方法之间不存在竞争条件。)

因为Promise.prototype.then和Promise.prototype.catch方法返回Promises对象, 所以它们可以被链式调用。

状态转换图

constructor

说明

语法

new Promise(executor);
new Promise(function(resolve, reject) { ... });

参数

namedesc
executor带有resolve、reject两个参数的函数对象。第一个参数用在处理执行成功的场景,第二个参数则用在处理执行失败的场景。一旦我们的操作完成即可调用这些函数。

实现

构造函数主要完成状态的初始化,并提供resolvereject两个方法给创建者以转变状态。

const utils = require('./utils');
const isFunction = utils.isFunction;const STATUS = {PENDING: 'pending',FULFILLED: 'fulfilled',REJECTED: 'rejected'
};const SYMBOL_STATUS = Symbol('status');
const SYMBOL_DATA = Symbol('data');
const SYMBOL_FULFILLED_CALLBACK = Symbol('fulfilledCallback');
const SYMBOL_REJECTED_CALLBACK = Symbol('rejectedCallback');class Promise {constructor(executor) {if (!isFunction(executor)) {throw Error(`Promise executor ${executor} is not a function`);}const self = this;// 初始状态为pendingconst status = self[SYMBOL_STATUS] = STATUS.PENDING;// 使用symbol实现私有化self[SYMBOL_FULFILLED_CALLBACK] = undefined;self[SYMBOL_REJECTED_CALLBACK] = undefined;self[SYMBOL_DATA] = undefined;function resovle(value) {// todo}function reject(reason) {// todo}// 执行executor函数,若出现异常则调用reject方法,将状态变为rejected,同时调用回调函数try {executor(resovle, reject);} catch (err) {reject(err);}}
}

上面代码中完成了构造函数的雏形,而resolvereject两个函数的职责为状态转变和回调函数的调用:

resolve,接受一个成功值,传递给绑定的fulfilled回调函数中。主要工作是将当前状态变为fulfilled状态,同时调用绑定的fulfilled回调函数:

function resovle(value) {const fulfilledCallback = self[SYMBOL_FULFILLED_CALLBACK];if (status === STATUS.PENDING) {self[SYMBOL_STATUS] = STATUS.FULFILLED;  // 状态转换self[SYMBOL_DATA] = value;  // 保存成功值if (isFunction(fulfilledCallback)) {setTimeout(() => {  // 不阻塞主流程,在下一个事件轮询中再调用fulfilled回调函数fulfilledCallback(value);});}}
}

reject,接受一个失败信息,传递给绑定的rejected回调函数中。主要工作是将当前状态变为rejected 状态,同时调用绑定的rejected回调函数:

function reject(reason) {const rejectedCallback = self[SYMBOL_REJECTED_CALLBACK];if (status === STATUS.PENDING) {self[SYMBOL_STATUS] = STATUS.REJECTED;  // 状态转换self[SYMBOL_DATA] = reason;  // 保存成功值if (isFunction(rejectedCallback)) {setTimeout(() => {  // 不阻塞主流程,在下一个事件轮询中再调用rejected回调函数rejectedCallback(reason);});}}
}

fulfilled`和rejected回调函数是通过Promise.prototype.then和Promise.prototype.catch注册的。

then

说明

then方法返回一个Promise。它有两个参数,分别为Promise在成功和失败情况下的回调函数。

语法

p.then(onFulfilled, onRejected);p.then(function(value) {// todo
}, function(reason) {// todo
});

参数

namedesc
onFulfilled一个函数,当Promise的状态为fulfilled时调用。该函数有一个参数,为成功的返回值。
onRejected一个函数,当Promise的状态为rejected时调用。该函数有一个参数,为失败的原因。

实现

根据当前状态对回调函数进行处理,同时返回一个新的Promise对象,以便链式调用。

then(onFulfilled, onRejected) {const self = this;const status = self[SYMBOL_STATUS];const data = self[SYMBOL_DATA];const resolveValue = self[SYMBOL_RESOLVE_VALUE];// 如果onFulfilled不是函数,回调函数仅返回成功值const fulfilledCallback = isFunction(onFulfilled)? onFulfilled: function returnFunc(value) { return value; };// 如果onRejected不是函数,回调函数仅返回失败信息const rejectedCallback = isFunction(onRejected)? onRejected: function throwFunc(reason) { throw reason; };// 当前状态为pendingif (status === STATUS.PENDING) {// 返回一个新的Promise对象,可以被链式调用return new Promise((resolve, reject) => {// 将fulfilled回调函数注册到当前Promise对象中(非新Promise对象)self[SYMBOL_FULFILLED_CALLBACK] = value => {// 根据回调函数的执行情况,通过传递的新Promise对象的resolve和reject方法对其状态进行转变try {const newValue = fulfilledCallback(value);// 解析成功值resolveValue(newValue, resolve, reject);} catch (err) {reject(err);}};// 同上self[SYMBOL_REJECTED_CALLBACK] = reason => {try {const newReason = rejectedCallback(reason);resolveValue(newReason, resolve, reject);} catch (err) {reject(err);}};});}// 当前状态为fulfilledif (status === STATUS.FULFILLED) {// 返回一个新的Promise对象,可以被链式调用return new Promise((resolve, reject) => {// 在下一个事件轮询中立即调用fulfilled回调函数,根据执行情况决定新Promise对象的状态转变setTimeout(() => {try {const newValue = fulfilledCallback(data);resolveValue(newValue, resolve, reject);} catch (err) {reject(err);}});});}// 当前状态为rejectedif (status === STATUS.REJECTED) {// 返回一个新的Promise对象,可以被链式调用return new Promise((resolve, reject) => {// 在下一个事件轮询中立即调用rejected回调函数,根据执行情况决定新Promise对象的状态转变setTimeout(() => {try {const newReason = rejectedCallback(data);resolveValue(newReason, resolve, reject);} catch (err) {reject(err);}});});}
}// 解析传递值函数
[SYMBOL_RESOLVE_VALUE](value, resolve, reject) {// 若传递值为Promise对象,将新Promise对象的resolve和reject传递给Promise传递值以触发状态的转换if (value instanceof Promise) {value.then(resolve, reject);return;}// 若传递值不是Promise对象,传递给resolve方法resolve(value);
}

根据当前Promise对象的状态,对回调函数进行注册或立即执行,同时返回一个新的Promise对象。

  • pending,注册回调函数到当前的Promise对象中

  • fulfilledrejected,立即执行回调函数

catch

说明

catch方法只处理Promise被拒绝的情况,并返回一个Promise。该方法的行为和调用Promise.prototype.then(undefined, onRejected)相同。

语法

p.catch(onRejected);p.catch(function(reason) {// todo
});

参数

namedesc
onRejected当Promise被拒绝时调用的函数。该函数调用时会传入一个参数:失败原因。

实现

catch方法是对then方法的包装,仅传递onRejected失败回调函数。

catch(onRejected) {return this.then(null, onRejected);
}

总结

现在回顾下Promise的实现过程,其主要使用了设计模式中的观察者模式:

  • 通过Promise.prototype.then和Promise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。

  • 被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者。

关键知识点

Promise

MDN

symbol

ruanyifeng

观察者模式

百度百科

资源

完整代码


http://lihuaxi.xjx100.cn/news/243786.html

相关文章

哪些人适合参加UI设计培训

UI设计在最近几年受到了很多人的关注,想要学习UI设计技术的人越来越多,大部分选择报UI设计培训班进行学习,有些人想要通过自学来学,那么到底哪些人适合参加UI设计培训呢?来看看下面的详细介绍。 哪些人适合参加UI设计培训? 1. 零…

50个云终端只需一台服务器是怎么一回事

看到这个标题也许有人会说50个云终端只需要一台服务器这应该是不可能的吧,即使是真的那这个服务器的配置和价格应该也要非常高的吧。但是如果有人和你说50个云终端只需要一台中等配置和价格的服务器就可以的呢。而且这50个用户桌面都可以正常的使用不会出现卡顿等现…

[BZOJ2796][Poi2012]Fibonacci Representation

由于是斐波那契数列&#xff0c;所以$x_ix_j<x_k,i<j<k$ 所以猜测可以贪心选择两边近的数处理。 1 #include<cstdio>2 #include<algorithm>3 #define ll long long4 #define mid (lr>>1)5 using namespace std;6 ll f[505],tot1;7 inline ll findl(…

SQLServer学习-- SQLServer

SQL Server 是Microsoft 公司推出的关系型数据库管理系统。具有使用方便可伸缩性好与相关软件集成程度高等优点&#xff0c;可跨越从运行Microsoft Windows 98 的膝上型电脑到运行Microsoft Windows 2012 的大型多处理器的服务器等多种平台使用。Microsoft SQL Server 是一个全…

零基础参加java培训如何学习

零基础的同学想要学好java技术&#xff0c;一定要比有基础的学员更加努力才可以&#xff0c;因为java技术要学习的东西有很多&#xff0c;在Java培训学习的过程中也是要掌握一定的技巧和方法的&#xff0c;下面就为大家详细的介绍一下零基础参加java培训如何学习? 零基础参加j…

小猿圈Linux学习-Linux种搜索的命令

做Linux工程师的每天都不能少的工作就是搜索文件&#xff0c;这是他们的日常活动&#xff0c;很繁琐很枯燥&#xff0c;所以我们就需要知道一些搜索的命令&#xff0c;这些命令更高效更快捷&#xff0c;今天小猿圈就给大家分享4个可以搜索的Linux命令。。 方法 1&#xff1a;使…

SQLServer查看存储过程的方法

使用 sp_helptext 查看存储过程的定义 在对象资源管理器中&#xff0c;连接到 数据库引擎实例&#xff0c;再展开该实例。在工具栏上&#xff0c;单击“新建查询”。在查询窗口中&#xff0c;输入下列语句。更改数据库名称和存储过程名称以引用所需的数据库和存储过程。USE yca…

Java培训一共分几个阶段

Java培训一共分几个阶段?一般培训机构的课程都是从基础知识讲起&#xff0c;千锋教育的Java培训课程也不例外&#xff0c;第一阶段为JavaEE基础&#xff0c;主要讲授Java基础语法、面向对象、核心类库、集合等等基础知识点&#xff0c;把基础打牢学习接下来的知识的时候才会更…