您现在的位置是:网站首页> 编程资料编程资料
JavaScript Promise原理与实现刨析_javascript技巧_
2023-05-24
244人已围观
简介 JavaScript Promise原理与实现刨析_javascript技巧_
1 Promise核心逻辑实现
Promise对象是一个原生的javascript对象,是一种异步编程的解决方案,它通过一个回调,可以避免更多的回调,接下来说明其原理及实现。
下面一段代码是Promise的基本使用:
new Promise((resolve, reject) => { resolve("成功"); reject("失败"); }) Promise.then(value => { }, reason => { }) 从上面的代码中,我们可以分析出一些关键点:
- Promise创建时需要使用
new关键字,那么我们可以知道Promise就是一个类; - 在执行这个类的时候,需要传递一个执行器进去,这个执行器会立即执行;
- 在执行器中有两个参数,
resolve和reject,它们都是函数,用来改变Promise中的状态; - Promise中有三种状态,分别是:成功
fulfilled、失败rejected和等待pending,状态只能从pending—>fulfilled,或者pending—>rejected,状态一旦确定就不可以更改; resolve和reject函数是用来更改状态的,其中,resolve将状态更改为fulfilled,reject将状态更改为rejected;then方法接收两个函数作为参数,它需要判断状态,如果成功调用第一个回调函数,如果失败调用第二个回调函数,并且then方法是被定义在原型对象中的,第一个回调函数的参数为成功之后的值,第二个回调函数的参数为失败之后的原因;
接下来我们根据上面分析出的内容,一步一步实现我们自己的Promise。
首先创建一个类,为constructor构造函数传入一个执行器,因为执行器需要立即执行,因此在构造函数中调用该执行器。
class MyPromise { constructor(executor) { // 接收一个执行器 executor(); // 执行器会立即执行 } } 在执行器中有两个参数resolve和reject,它们都是函数,因此在类中创建两个箭头函数resolve和reject,在执行器executor中使用this来调用它们。
为什么使用箭头函数:
注意,我们在Promise中调用resolve和reject是直接调用的,如果将它们写成普通函数,那么会将this指向window或者undefined,如果我们写成箭头函数,那么它们的this就会指向类的实例对象。
class MyPromise { constructor(executor) { // 接收一个执行器 executor(this.resolve, this.reject); // 执行器会立即执行 } resolve = () => { } reject = () => { } } resolve和reject这两个函数是用来改变状态的,因此我们将状态定义在类的外面,因为它们会被频繁使用到。在类中我们默认定义状态status是等待pending,当调用resolve时,状态改为成功,当调用reject时,状态改为失败。并且状态一旦确定不可更改,因此我们要在两个函数中判断当前状态是否为等待,不是则返回。
const PENDING = "pending"; // 等待 const FULFILLED = "fulfilled"; // 成功 const REJECTED = "rejected"; // 失败 class MyPromise { constructor(executor) { // 接收一个执行器 executor(this.resolve, this.reject); // 执行器会立即执行 } status = PENDING; // 状态默认为pending等待 resolve = () => { if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = FULFILLED; // 将状态改为成功 } reject = () => { if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = REJECTED; // 将状态改为失败 } }Promise中的then方法有两个参数,当状态成功调用第一个,状态失败调用第二个,因此内部需要使用if来判断状态。调用成功或者失败函数时,需要为其传入参数,那么我们知道成功的值是由resolve传递来的,失败的原因是由reject传递来的,因此我们在Promise中声明两个属性存放两个值。
const PENDING = "pending"; // 等待 const FULFILLED = "fulfilled"; // 成功 const REJECTED = "rejected"; // 失败 class MyPromise { constructor(executor) { // 接收一个执行器 executor(this.resolve, this.reject); // 执行器会立即执行 } status = PENDING; // 状态默认为pending等待 value = undefined; // 成功之后的值 reason = undefined; // 失败之后的原因 resolve = value => { // value是成功之后的值 if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = FULFILLED; // 将状态改为成功 this.value = value; // 将成功的值传递 } reject = reason => { // reason是失败之后的原因 if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = REJECTED; // 将状态改为失败 this.reason = reason; // 将失败的值传递 } then(successCallback, failCallback) { // then方法有两个参数 if (this.status === FULFILLED) { // 成功调用第一个回调函数 successCallback(this.value); } else if (this.status === REJECTED) { // 失败调用第二个回调函数 failCallback(this.reason); } } }到这里我们就实现了一个最简单的Promise了。
2 加入异步逻辑
上面我们实现的Promise,实际上并没有考虑异步情况,比如说下面的代码中,2秒后调用成功的回调,如果这时调用then方法,那么当前的状态是等待pending,但是我们并没有判断状态是pending时的情况。
new Promise((resolve, reject) => { setTimeout(() => { resolve("成功"); }, 2000); }) Promise.then(value => { }, reason => { }) 因此在then方法中,我们应该判断当状态是等待的情况。当状态是等待时,我们没有办法调用成功或者失败的回调,这时我们需要将成功回调和失败回调储存起来。
const PENDING = "pending"; // 等待 const FULFILLED = "fulfilled"; // 成功 const REJECTED = "rejected"; // 失败 class MyPromise { constructor(executor) { // 接收一个执行器 executor(this.resolve, this.reject); // 执行器会立即执行 } status = PENDING; // 状态默认为pending等待 value = undefined; // 成功之后的值 reason = undefined; // 失败之后的原因 successCallback = undefined; // 成功的回调函数 failCallback = undefined; // 失败的回调函数 resolve = value => { // value是成功之后的值 if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = FULFILLED; // 将状态改为成功 this.value = value; // 将成功的值传递 } reject = reason => { // reason是失败之后的原因 if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = REJECTED; // 将状态改为失败 this.reason = reason; // 将失败的值传递 } then(successCallback, failCallback) { // then方法有两个参数 if (this.status === FULFILLED) { // 成功调用第一个回调函数 successCallback(this.value); } else if (this.status === REJECTED) { // 失败调用第二个回调函数 failCallback(this.reason); } else { // 当状态为等待时,将成功回调和失败回调存储起来 this.successCallback = successCallback; this.failCallback = failCallback; } } }将成功回调和失败回调存储起来之后,我们则要在resolve和reject方法中判断是否存在成功或者失败的回调,如果存在,则将其调用。
const PENDING = "pending"; // 等待 const FULFILLED = "fulfilled"; // 成功 const REJECTED = "rejected"; // 失败 class MyPromise { constructor(executor) { // 接收一个执行器 executor(this.resolve, this.reject); // 执行器会立即执行 } status = PENDING; // 状态默认为pending等待 value = undefined; // 成功之后的值 reason = undefined; // 失败之后的原因 successCallback = undefined; // 成功的回调函数 failCallback = undefined; // 失败的回调函数 resolve = value => { // value是成功之后的值 if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = FULFILLED; // 将状态改为成功 this.value = value; // 将成功的值传递 this.successCallback && this.successCallback(this.value); // 如果成功回调存在,则调用 } reject = reason => { // reason是失败之后的原因 if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = REJECTED; // 将状态改为失败 this.reason = reason; // 将失败的值传递 this.failCallback && this.failCallback(this.reason); // 如果失败回调存在,则调用 } then(successCallback, failCallback) { // then方法有两个参数 if (this.status === FULFILLED) { // 成功调用第一个回调函数 successCallback(this.value); } else if (this.status === REJECTED) { // 失败调用第二个回调函数 failCallback(this.reason); } else { // 当状态为等待时,将成功回调和失败回调存储起来 this.successCallback = successCallback; this.failCallback = failCallback; } } }这是我们就处理了异步的情况了。
3 then方法添加多次调用逻辑
Promise的then方法可以调用多次,我们接着处理这部分。
let promise = new Promise((resolve, reject) => { }) promise.then(value => { }) promise.then(value => { }) promise.then(value => { })如果多次调用了then方法,就需要考虑两种情况:同步情况和异步情况。如果是同步情况,那么直接就可以调用回调函数,我们已经不需要多做处理了,如果是异步情况,那么我们需要将每一个回调函数储存起来。
我们之前在then方法中判断等待的时候,也将成功和失败的回调存储起来,但是每次只能存储一个,因此我们需要将存储的容器设为数组,通过数组的push方法将回调函数存储起来。
const PENDING = "pending"; // 等待 const FULFILLED = "fulfilled"; // 成功 const REJECTED = "rejected"; // 失败 class MyPromise { constructor(executor) { // 接收一个执行器 executor(this.resolve, this.reject); // 执行器会立即执行 } status = PENDING; // 状态默认为pending等待 value = undefined; // 成功之后的值 reason = undefined; // 失败之后的原因 successCallback = []; // 使用数组存储成功的回调函数 failCallback = []; // 使用数组存储失败的回调函数 resolve = value => { // value是成功之后的值 if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = FULFILLED; // 将状态改为成功 this.value = value; // 将成功的值传递 this.successCallback && this.successCallback(this.value); // 如果成功回调存在,则调用 } reject = reason => { // reason是失败之后的原因 if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = REJECTED; // 将状态改为失败 this.reason = reason; // 将失败的值传递 this.failCallback && this.failCallback(this.reason); // 如果失败回调存在,则调用 } then(successCallback, failCallback) { // then方法有两个参数 if (this.status === FULFILLED) { // 成功调用第一个回调函数 successCallback(this.value); } else if (this.status === REJECTED) { // 失败调用第二个回调函数 failCallback(this.reason); } else { // 当状态为等待时,将成功回调和失败回调存储起来 this.successCallback.push(successCallback); this.failCallback.push(failCallback); } } }更改成数组之后,那么我们原来在resolve和reject函数中调用成功或者失败的回调函数就不可以使用了,而是在其中循环调用数组中的回调函数。
const PENDING = "pending"; // 等待 const FULFILLED = "fulfilled"; // 成功 const REJECTED = "rejected"; // 失败 class MyPromise { constructor(executor) { // 接收一个执行器 executor(this.resolve, this.reject); // 执行器会立即执行 } status = PENDING; // 状态默认为pending等待 value = undefined; // 成功之后的值 reason = undefined; // 失败之后的原因 successCallback = []; // 成功的回调函数 failCallback = []; // 失败的回调函数 resolve = value => { // value是成功之后的值 if (this.status !== PENDING) return; // 当状态不是等待,直接返回 this.status = FULFILLED; // 将状态改为成功 this.value = value; // 将成功的值传递 while (this.successCallback.length) { // 循环执行数组中的回调函数 this.successCallback.shift()(this.value); // 调用回调函数 } } reject = reason => { // reason是失败之后的原因 if (this.status !==
相关内容
- react路由v6版本NavLink的两个小坑及解决_React_
- Vue事件修饰符使用详细介绍_vue.js_
- JavaScript自定义Webpack配置实现流程介绍_javascript技巧_
- vue使用Print.js打印页面样式不出现的解决_vue.js_
- vue 使用print-js 打印渲染不出来问题_vue.js_
- Vue3+Element Plus按需引入(自动导入)详解_vue.js_
- el-select与el-tree结合使用实现树形结构多选框_vue.js_
- 一文教会你vue中使用async和await_vue.js_
- element Drawer 抽屉无法渲染问题及解决_vue.js_
- vue如何动态加载组件详解_vue.js_
点击排行
本栏推荐
