请你讲一下Promise是如何实现的?

1. 前言

面试的时候经常会遇到面试官问Promise是如何实现的?面试官想了解的是你对Promise对象的掌握,如果你能够从Promise是什么,有什么特点,内部实现包括then方法的链式调用实现来回答,肯定会让面试官满意的。

本文将尝试由浅入深,手写一个符合规范的Promise对象,希望对大家面试和理解Promise有所帮助。

2. Promise 是什么

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。

Promise出现之前都是通过回调函数来实现,回调函数本身没有问题,但是嵌套层级过深,很容易掉进回调地狱

const fs = require('fs');
fs.readFile('1.txt', (err,data) => {
    fs.readFile('2.txt', (err,data) => {
        fs.readFile('3.txt', (err,data) => {
            //可能还有后续代码
        });
    });
});

如果每次读取文件后还要进行逻辑的判断或者异常的处理,那么整个回调函数就会非常复杂且难以维护。

Promise的出现正是为了解决这个痛点,我们可以把上面的回调嵌套用Promise改写一下:

// 使用Promise读取文件
const readFile = function(fileName){
    return new Promise((resolve, reject)=>{
        fs.readFile(fileName, (err, data)=>{
            if(err){
                reject(err)
            } else {
                resolve(data)
            }
        })
    })
}




// 调用
readFile('1.txt')
    .then(data => {
        return readFile('2.txt');
    }).then(data => {
        return readFile('3.txt');
    }).then(data => {
        //...
    });

3. Promise 基本结构

我们先回顾一下,我们平时都是怎么使用Promise的:

var p = new Promise(function(resolve, reject){
    console.log('执行')
    setTimeout(function(){
        resolve(2)
    }, 1000)
})

p.then(function(res){
    console.log('suc',res)
},function(err){
    console.log('err',err)
})

首先看出来,Promise是通过构造函数实例化一个对象,然后通过实例对象上的then方法,来处理异步返回的结果。

4. Promise 对象的特点

4.1. 对象的状态不受外界影响

Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变

const PENDING = 'pending'


const FULFILLED = 'fulfilled'


const REJECTED = 'rejected'




function Promise(executor) {
    var This = this
    this.state = PENDING; //状态
    this.value = undefined; //成功结果
    this.reason = undefined; //失败原因
    function resolve(value) {}
    function reject(reason) {}
}




Promise.prototype.then = function (onFulfilled, onRejected) {};

module.exports = Promise;

当我们实例化Promise时,构造函数会马上调用传入的执行函数executor,我们可以试一下:

let p = new Promise((resolve, reject) => {
    console.log('执行了');
});

因此在Promise中构造函数立马执行,同时将resolve函数和reject函数作为参数传入:

function Promise(executor) {

    var This = this
    this.state = PENDING; //状态
    this.value = undefined; //成功结果
    this.reason = undefined; //失败原因
    function resolve(value) {}
    function reject(reason) {}
    executor(resolve, reject)
}

但是executor也会可能存在异常,因此通过try/catch来捕获一下异常情况:

try {
    executor(resolve, reject);
} catch (e) {
    reject(e);
}

4.2. 状态不可再变

一旦状态改变,就不会再变,任何时候都可以得到这个结果。

Promise对象的状态改变,只有两种可能:从pending变为fulfilled从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。

所以Promise在回调函数resolvereject中判断,只能是pending状态的时候才能更改状态:

function resolve(value) {

    if(This.state === PENDING){

        This.state = FULFILLED

        This.value = value

    }
}
function reject(reason) {
    if(This.state === PENDING){
        This.state = REJECTED
        This.reason = reason
    }

}


在更改状态的同时,将回调函数中成功的结果或者失败的原因都保存在对应的属性中,方便以后来获取。

5. 实现支持异步的then方法

Promise的状态改变之后,不管成功还是失败,都会触发then回调函数。因此,then的实现也很简单,就是根据状态的不同,来调用不同处理终值的函数。

Promise.prototype.then = function (onFulfilled, onRejected) {


    if(this.state === FULFILLED){

        typeof onFulfilled === 'function' && onFulfilled(this.value)

    }

    if(this.state === REJECTED){

        typeof onRejected === 'function' && onRejected(this.reason)

    }

};

onFulfilled和onRejected是可选的,因此我们对两个值进行一下类型的判断:

onFulfilled 和 onRejected 都是可选参数。如果 onFulfilled 不是函数,其必须被忽略。如果 onRejected 不是函数,其必须被忽略。

到这一步我们基本已经完成了Promise的代码,但是它还不能工作,因为当then里面函数运行时,resolve由于是异步执行的,还没有来得及修改state,此时还是PENDING状态;因此我们需要对异步的情况做一下处理。

那么如何让我们的Promise来支持异步呢?我们可以参考发布订阅模式,在执行then方法的时候,如果当前还是PENDING状态,就把回调函数寄存到一个数组中,当状态发生改变时,去数组中取出回调函数;因此我们先在Promise中定义一下变量:

function Promise(executor) {

    this.onFulfilled = [];//成功的回调
    this.onRejected = []; //失败的回调
}

这样,当then执行时,如果还是PENDING状态,我们不是马上去执行回调函数,而是将其存储起来:

Promise.prototype.then = function (onFulfilled, onRejected) {


    if(this.state === FULFILLED){

        typeof onFulfilled === 'function' && onFulfilled(this.value)

    }

    if(this.state === REJECTED){

        typeof onRejected === 'function' && onRejected(this.reason)

    }

    if(this.state === PENDING){
        typeof onFulfilled === 'function' && this.onFulfilled.push(onFulfilled)
        typeof onRejected === 'function' && this.onRejected.push(onRejected)
    }

};

存储起来后,当resolve或者reject异步执行的时候就可以来调用了:

function resolve(value) {

    if(This.state === PENDING){

        This.state = FULFILLED

        This.value = value

        This.onFulfilled.forEach(fn => fn(value))
    }
}


function reject(reason) {
    if(This.state === PENDING){
        This.state = REJECTED
        This.reason = reason
        This.onRejected.forEach(fn => fn(reason))
    }
}

有同学可能会提出疑问了,为什么这边onFulfilledonRejected要存在数组中,直接用一个变量接收不是也可以么?下面看一个例子:

var p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve(4)
    }, 0)
})
p.then((res)=>{
    //4 res
    console.log(res, 'res')
})
p.then((res1)=>{
    //4 res1
    console.log(res1, 'res1')
})

我们分别调用了两次then,如果是一个变量的话,最后肯定只会运行后一个then,把之前的覆盖了,如果是数组的话,两个then都能正常运行。

至此,我们运行demo,就能如愿以偿的看到运行结果了;一个四十行左右的简单Promise就此完成了。

这里贴一下完整的代码:

const PENDING = 'pending'


const FULFILLED = 'fulfilled'


const REJECTED = 'rejected'


function Promise(executor) {
    var This = this
    this.state = PENDING; //状态
    this.value = undefined; //成功结果
    this.reason = undefined; //失败原因

    this.onFulfilled = [];//成功的回调
    this.onRejected = []; //失败的回调
    function resolve(value) {
        if(This.state === PENDING){
            This.state = FULFILLED
            This.value = value
            This.onFulfilled.forEach(fn => fn(value))
        }
    }
    function reject(reason) {
        if(This.state === PENDING){
            This.state = REJECTED
            This.reason = reason
            This.onRejected.forEach(fn => fn(reason))
        }
    }
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}
Promise.prototype.then = function (onFulfilled, onRejected) {
    if(this.state === FULFILLED){
        typeof onFulfilled === 'function' && onFulfilled(this.value)
    }

    if(this.state === REJECTED){
        typeof onRejected === 'function' && onRejected(this.reason)
    }
    if(this.state === PENDING){
        typeof onFulfilled === 'function' && this.onFulfilled.push(onFulfilled)
        typeof onRejected === 'function' && this.onRejected.push(onRejected)
    }

};

// 导出模块,因为我的demo是在node控制台运行的,所以这导出成node支持的模块
module.exports = Promise;

Demo代码:

const Promise = require('./Promise')

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('hello')
    }, 0)
})


p1.then(res => {
    console.log('res0 => ', res)
})

p1.then(res => {
    console.log('res1 => ', res)
})

// 运行结果
// res0 =>  hello
// res1 =>  hello

6. 让then方法支持链式调用

then方法必须返回一个新的Promise对象,这样才能实现then方法的链式调用,上面的代码什么都没有返回,所以是不支持链式调用的,让我们对then方法做一些改写。

Promise.prototype.then = function (onFulfilled, onRejected) {


    // 不论then进行什么操作,都返回一个新的Promise对象    
    var This = this // This是myPromise的实例对象
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
    
  	// 定义resolvePromise方法
    function resolvePromise(myPromise, fulfilled, resolve, reject) {
        // 避免调用死循环
        if(myPromise === fulfilled){
            reject(new TypeError('Chaining cycle'))
        }
        // 判断fulfilled是对象还是方法
        if(fulfilled && typeof fulfilled === 'object' || typeof fulfilled === 'function'){
            let used;
            // 防止使用Object.defineProperty()恶意抛错,这里使用try/catch预防
            try {
                let then = fulfilled.then
                if(typeof then === 'function'){
                    then.call(fulfilled, (res)=>{
                        if (used) return;
                        used = true
                        resolvePromise(myPromise, res, resolve, reject)
                    }, (r) =>{
                        if (used) return;
                        used = true
                        reject(r)
                    })
                } else {
                    if (used) return;
                    used = true
                    resolve(fulfilled)
                }
            } catch(e){
                if (used) return;
                used = true
                reject(e)
            }
        } else {
            resolve(fulfilled)
        }
    }


    // 让then方法返回一个新的Promise对象
    const myPromise = new Promise((resolve, reject) => {
        if(This.state === FULFILLED){
            setTimeout(()=>{
                try {
                    let fulfilled = onFulfilled(This.value)
                    resolvePromise(myPromise, fulfilled, resolve, reject)
                } catch (error) {
                    reject(error)
                }
            })
        } else if(This.state === REJECTED){
            setTimeout(()=>{
                try {                    
                    let fulfilled = onRejected(This.reason)
                    resolvePromise(myPromise, fulfilled, resolve, reject)
                } catch (error) {
                    reject(error)
                }
            })
        } else if(This.state === PENDING){
            This.onFulfilled.push(()=>{
                setTimeout(()=>{
                    try {                        
                        let fulfilled = onFulfilled(This.value)
                        resolvePromise(myPromise, fulfilled, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
          
            This.onRejected.push(()=>{
                setTimeout(()=>{
                    try {                        
                        let fulfilled = onRejected(This.reason)
                        resolvePromise(myPromise, fulfilled, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
        }
  	})
  
  	return myPromise
}

7. 最终版完整代码

const PENDING = 'pending'


const FULFILLED = 'fulfilled'


const REJECTED = 'rejected'




// 定义对象的构造函数
function Promise(executor) {
    var This = this
    this.state = PENDING; //状态
    this.value = undefined; //成功结果
    this.reason = undefined; //失败原因

    this.onFulfilled = [];//成功的回调
    this.onRejected = []; //失败的回调
  
    function resolve(value) {
        if(This.state === PENDING){
            This.state = FULFILLED
            This.value = value
            This.onFulfilled.forEach(fn => fn(value))
        }
    }
  
    function reject(reason) {
        if(This.state === PENDING){
            This.state = REJECTED
            This.reason = reason
            This.onRejected.forEach(fn => fn(reason))
        }
    }
  
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }

}

// 实现then方法
Promise.prototype.then = function (onFulfilled, onRejected) {
    // 不论then进行什么操作,都返回一个新的Promise对象    
    var This = this // This是myPromise的实例对象
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
    
  	// 定义resolvePromise方法
    function resolvePromise(myPromise, fulfilled, resolve, reject) {
        // 避免调用死循环
        if(myPromise === fulfilled){
            reject(new TypeError('Chaining cycle'))
        }
        // 判断fulfilled是对象还是方法
        if(fulfilled && typeof fulfilled === 'object' || typeof fulfilled === 'function'){
            let used;
            // 防止使用Object.defineProperty()恶意抛错,这里使用try/catch预防
            try {
                let then = fulfilled.then
                if(typeof then === 'function'){
                    then.call(fulfilled, (res)=>{
                        if (used) return;
                        used = true
                        resolvePromise(myPromise, res, resolve, reject)
                    }, (r) =>{
                        if (used) return;
                        used = true
                        reject(r)
                    })
                } else {
                    if (used) return;
                    used = true
                    resolve(fulfilled)
                }
            } catch(e){
                if (used) return;
                used = true
                reject(e)
            }
        } else {
            resolve(fulfilled)
        }
    }

    // 让then方法返回一个新的Promise对象
    const myPromise = new Promise((resolve, reject) => {
        if(This.state === FULFILLED){
            setTimeout(()=>{
                try {
                    let fulfilled = onFulfilled(This.value)
                    resolvePromise(myPromise, fulfilled, resolve, reject)
                } catch (error) {
                    reject(error)
                }
            })
        } else if(This.state === REJECTED){
            setTimeout(()=>{
                try {                    
                    let fulfilled = onRejected(This.reason)
                    resolvePromise(myPromise, fulfilled, resolve, reject)
                } catch (error) {
                    reject(error)
                }
            })
        } else if(This.state === PENDING){
            This.onFulfilled.push(()=>{
                setTimeout(()=>{
                    try {                        
                        let fulfilled = onFulfilled(This.value)
                        resolvePromise(myPromise, fulfilled, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
          
            This.onRejected.push(()=>{
                setTimeout(()=>{
                    try {                        
                        let fulfilled = onRejected(This.reason)
                        resolvePromise(myPromise, fulfilled, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
        }
  	})
  
  	return myPromise
}

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYLI5fgX' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片