likes
comments
collection
share

带着问题寻找答案,实现源码也许会更加轻松

作者站长头像
站长
· 阅读数 2

对于Promise当中的一些特性,我们需要了解并且掌握,今天我们来根据特性一步一步的实现我们的Promise源码,更好的理解并且掌握它

1、resolve reject 实现

//html
let p = new Promise((resolve, reject) => {
    resolve('OK11');  
    resolve('OK');  
    //reject('rrr')
});
console.log(p)
//js
class Promise{
    constructor(executor){
        let _this = this
        _this.PromiseState = 'pending' //用于保存状态
        _this.PromiseResult = null //用于保存结果
        let resolve = function(data){
            _this.PromiseState = 'success'//没有使用箭头函数,this指向要改变
            _this.PromiseResult = data
        }
        let reject = function(data){
            _this.PromiseState = 'rejected'
            _this.PromiseResult = data
        }
        executor(resolve,reject)
    }

一开始对于executor(resolve,reject)这一段不太理解,可以理解为new Promise实例化的时候传入一个函数,放到construtor中进行执行,函数需要传入俩个参数,需要用resolve和reject两个函数当做参数,这里在传入的函数中调用resolve即调用constructor中的resolve

来看下控制台打印结果

带着问题寻找答案,实现源码也许会更加轻松

2、只能改变一次状态

Promise中只能改变一次状态,所以每次改变状态前判断一下状态是否已经改变了,这个比较简单

//html
let p = new Promise((resolve, reject) => {
    resolve('OK11');  
    resolve('OK');  
});
//js
let resolve = function(data){
+    if(_this.PromiseState!='pending') return
     ...
}
let reject = function(data){
+    if(_this.PromiseState!='pending') return
     ...
}

现在只会执行一次resolve 带着问题寻找答案,实现源码也许会更加轻松

3、抛出错误改变

Promise中如果没有改变状态而是抛出错误,需要将状态改变为reject,那么我们在executor执行的时候捕获一下错误,如果有错误,改变状态

let p = new Promise((resolve, reject) => {
-    resolve('OK11');  
-    resolve('OK');  
+    throw 'error'
});
class Promise{
    constructor(executor){
+        try {
            executor(resolve,reject)
+        } catch (error) {
+            reject(error)
+        }
+    }

带着问题寻找答案,实现源码也许会更加轻松

4、then实现

当进入到then的时候 promise同步状态下 执行到then的时候状态是已经改变的,then中传入两个函数,我们根据状态的不同调用不同的函数,调用函数的时候有一个value值,

这个value是执行resolve('OK')的时候已经将ok保存到PromiseResult中,所以在then中输出value的时候,其实就是输出PromiseResult

//html
let p = new Promise((resolve, reject) => {
    resolve('OK');  
});
p.then(value => {
    console.log(value)
}, reason=>{
    console.warn(reason)
})

//js
+ then(onResolved,onRejected){
+   if(this.PromiseState=='success'){
+        onResolved(this.PromiseResult)
+    }
+    if(this.PromiseState=='rejected'){
+        onRejected(this.PromiseResult)
+    }
}

此时我们的OK已经打印出来 带着问题寻找答案,实现源码也许会更加轻松

5.异步调用时候then中的问题

以下这段代码不会输出ok,因为在then执行的时候,由于事件循环,then调用的时候resolve还未调用,所以在then中PromiseState的状态仍然未pending,因此不会输出,所以我们需要加一个判断

let p = new Promise((resolve, reject) => {
    setTimeout(()=>{
        resolve('OK');  
    },100)
});
p.then(value => {
    console.log(value)
}, reason=>{
    console.warn(reason)
})

p.then(value => {
    console.log(2222)
}, reason=>{
    console.warn(reason)
})

对于then执行的时候状态仍未pending的状态,我们将回调存到回调列表中,等待resolve调用的时候再去执行所有回调,之所以用数组保存,因为可能有多个then回调

constructor(executor){
+    _this.callbacks = []
    let resolve = function(data){
+        _this.callbacks.forEach((item)=>{
+            item.onResolve(_this.PromiseResult)
+        })
    }
    let reject = function(data){
+        _this.callbacks.forEach((item)=>{
+            item.onReject(_this.PromiseResult)
+        })
    }
then(onResolve,onReject){
+    if(this.PromiseState=='pending'){//处理resolved异步比then慢执行的时候
+        this.callbacks.push({onResolve,onReject})
+    } 
} 

带着问题寻找答案,实现源码也许会更加轻松

实现promise的返回值

如果返回值是一个非promise对象,返回状态设置为成功, 如果返回值是一个异常,返回状态设置为失败

我们知道then中返回的也是一个Promise,所以我们给then中返回一个Promise,只需要在promise中做两件事情,执行回调,改变状态

如果返回值是一个promise,此时我们的promise已经有then方法了,我们直接用then方法来处理状态的改变即可,最后加上一层try的错误检验,reject的方法相同

let p = new Promise((resolve, reject) => {
    resolve(1);  
});
let res = p.then(value => {
    return new Promise((resolve,reject)=>{
        resolve(2)
    })
}, reason=>{
    console.warn(reason)
})
console.log(p)
console.log(res)

//js
    then(onResolve,onReject){
        if(this.PromiseState=='pending'){
            this.callbacks.push({onResolve,onReject})
        } 
        return new Promise((resolve,reject)=>{
            if(this.PromiseState=='success'){
                 try {
                    let res = onResolve(this.PromiseResult)//执行回调
                    if(res instanceof Promise){
                        res.then((v)=>{//之所以加这一层 是因为promise状态改变了 但是里面的值没输出 所以要加个then,不然就成了promise里面包裹着一个pormise
                            resolve(v)//改变状态
                        },(r)=>{
                            reject(r)//改变状态
                        })
                    }else{
                        resolve(res)//改变状态
                    }
                 } catch (error) {
                     reject(error)
                 }  
            }
        })
    }

执行一下看看

let p = new Promise((resolve, reject) => {
    resolve(1);  
});
let res = p.then(value => {
    return new Promise((resolve,reject)=>{
        resolve(2)
    })
}, reason=>{
    console.warn(reason)
})
console.log(p)
console.log(res)

如果没有判断res instanceof Promise ,第二个then的会输出一个promise

带着问题寻找答案,实现源码也许会更加轻松

加上判断后的结果

带着问题寻找答案,实现源码也许会更加轻松

一开始我在判断返回值为promise这个地方一直搞不懂,为啥要执行then,为啥调用resolve,所以在写源码的时候应该多动手打印、debugger输出,下面是我对这段代码的解释

return new Promise((resolve,reject)=>{
...
if (result instanceof Promise) {//如果是 Promise 类型的对象
    result.then(v => {
        //之所以加这一层 是以为result是一个promise,
        // 他的成功和失败决定了外层promise的状态(也就是真正then返回的promise)
        //那么他成功 则调用外层的成功
        resolve(v);
    }, r => {
        reject(r);
    })
}