Promise原理及实现

Posted 橘猫吃不胖~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Promise原理及实现相关的知识,希望对你有一定的参考价值。

Promise原理及实现

1 Promise核心逻辑实现

Promise对象是一个原生的javascript对象,是一种异步编程的解决方案,它通过一个回调,可以避免更多的回调,接下来说明其原理及实现。

下面一段代码是Promise的基本使用:

new Promise((resolve, reject) => 
    resolve("成功");
    reject("失败");
)
Promise.then(value =>  , reason =>  )

从上面的代码中,我们可以分析出一些关键点:

  1. Promise创建时需要使用new关键字,那么我们可以知道Promise就是一个类
  2. 在执行这个类的时候,需要传递一个执行器进去,这个执行器会立即执行
  3. 在执行器中有两个参数,resolvereject,它们都是函数,用来改变Promise中的状态
  4. Promise中有三种状态,分别是:成功fulfilled、失败rejected和等待pending,状态只能从pending—>fulfilled,或者pending—>rejected状态一旦确定就不可以更改
  5. resolvereject函数是用来更改状态的,其中,resolve将状态更改为fulfilledreject将状态更改为rejected
  6. then方法接收两个函数作为参数,它需要判断状态,如果成功调用第一个回调函数,如果失败调用第二个回调函数,并且then方法是被定义在原型对象中的,第一个回调函数的参数为成功之后的值,第二个回调函数的参数为失败之后的原因;

接下来我们根据上面分析出的内容,一步一步实现我们自己的Promise。

首先创建一个类,为constructor构造函数传入一个执行器,因为执行器需要立即执行,因此在构造函数中调用该执行器。

class MyPromise 
    constructor(executor)  // 接收一个执行器
        executor(); // 执行器会立即执行
    

在执行器中有两个参数resolvereject,它们都是函数,因此在类中创建两个箭头函数resolvereject,在执行器executor中使用this来调用它们。

为什么使用箭头函数:
注意,我们在Promise中调用resolvereject是直接调用的,如果将它们写成普通函数,那么会将this指向window或者undefined,如果我们写成箭头函数,那么它们的this就会指向类的实例对象。

class MyPromise 
    constructor(executor)  // 接收一个执行器
        executor(this.resolve, this.reject); // 执行器会立即执行
    
    resolve = () => 

    
    reject = () => 

    

resolvereject这两个函数是用来改变状态的,因此我们将状态定义在类的外面,因为它们会被频繁使用到。在类中我们默认定义状态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;
        
    

将成功回调和失败回调存储起来之后,我们则要在resolvereject方法中判断是否存在成功或者失败的回调,如果存在,则将其调用。

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);
        
    

更改成数组之后,那么我们原来在resolvereject函数中调用成功或者失败的回调函数就不可以使用了,而是在其中循环调用数组中的回调函数。

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 !== PENDING) return; // 当状态不是等待,直接返回
        this.status = REJECTED; // 将状态改为失败
        this.reason = reason; // 将失败的值传递
        while (this.failCallback.length)  // 循环执行
            this.failCallback.shift()(this.value); // 调用失败回调函数
        
    

    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);
        
    

4 链式调用then方法

Promise的then方法可以链式调用,下一个then方法中成功回调函数的参数是上一个then方法中的回调函数的返回值,也就是说,在下面代码中,value的值为1。

let promise = new Promise((resolve, reject) =>  );
promise.then(() =>  return 1 )
    .then(value =>  )
    .then(() =>  )

我们首先来实现then方法的链式调用。then方法是Promise中的方法,如果要实现链式调用,那么每个then方法都应该返回一个Promise对象,这样才可以调用。那么我们应该在then方法中创建一个Promise对象,最后返回这个对象就可以。除此之外,我们还需要将then方法中原来的代码传入到新创建对象的执行器中,保证调用方法后就立即执行。

then(successCallback, failCallbackvue结合Promise及async实现高效开发。

async/await 原理及简单实现

promise的原理及面试题

怎么用promise实现异步控制

promise之nodejsQ的详细用法总结

Promise 原理