手写一个Promise【前端每日一题-24】

Promise的原理以及手写一个Promise

观察原生promise用法,咱们能够发现,在new Promise时候传入了一个函数,这个函数在规范中的叫法是exector 执行器
看到这里,咱们先有一个大概思路,构建一个本身的Promise构造函数。
javascript

// 这里咱们建立了一个构造函数 参数就是执行器
function Promise(exector) {
    
}复制代码

好的,第一步完成, 重点来了,这个Promise内部到底干了什么呢 能够看到,原生的exector中传入了两个参数,第一个参数执行会让promise状态变为resolve, 也就是成功, 第二个执行会让函数变为reject状态,也就是失败java

而且这连个形参执行以后均可以传入参数,咱们继续完善代码 咱们将这两个形参的函数封装在构造函数内部数组

// 这里咱们建立了一个构造函数 参数就是执行器
function Promise(exector) {
    // 这里咱们将value 成功时候的值 reason失败时候的值放入属性中
    let self = this;
    this.value = undefined;
    this.reason = undefined;
    
    // 成功执行
    function resolve(value) {
      self.value = value;
    }
    
    // 失败执行
    function reject(reason) {
        self.reason = reason;
    }
    
    exector(resolve, reject);
}复制代码

这里问题来了,咱们知道,promise的执行过程是不可逆的,resolve和rejeact之间也不能相互转化, 这里,咱们就须要加入一个状态,判断当前是否在pending过程,另外咱们的执行器可能直接报错,这里咱们也须要处理一下。

// 这里咱们建立了一个构造函数 参数就是执行器
function Promise(exector) {
    // 这里咱们将value 成功时候的值 reason失败时候的值放入属性中
    let self = this;
    // 这里咱们加入一个状态标识
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    
    // 成功执行
    function resolve(value) {
        // 判断是否处于pending状态
        if (self.status === 'pending') {
            self.value = value;
            // 这里咱们执行以后须要更改状态
            self.status = 'resolved';
        }
    }
    
    // 失败执行
    function reject(reason) {
        // 判断是否处于pending状态
        if (self.status === 'pending') {
            self.reason = reason;
            // 这里咱们执行以后须要更改状态
            self.status = 'rejected';
        }
    }
    
    // 这里对异常进行处理
     try {
        exector(resolve, reject);
    } catch(e) {
        reject(e)
    }
}复制代码

new Promise以后咱们怎么去改变promise对象的状态呢, 经过原生的用法咱们了解到,须要使用then方法, then方法分别指定了resolved状态和rejeacted状态的回调函数
那怎么知道使用哪一个回调函数呢,咱们刚不是在构造函数内部定义了status么,这里就用上啦,上代码

// 咱们将then方法添加到构造函数的原型上 参数分别为成功和失败的回调

Promise.prototype.then = function(onFulfilled, onRejected) {
    // 获取下this
    let self = this;
    if (this.status === 'resolved') {
        onFulfilled(self.value);
    }
    
    if (this.status === 'rejected') {
        onRejected(self.reason);
    }
}复制代码

ok,咱们如今能够本身运行试试

let promise = new Promise((resolve, reject) => {
     resolve("haha");
})


promise.then(data => {
    console.log(data); //输出 haha
}, err=> {
    console.log(err);
})

// 屡次调用
promise.then(data => {
    console.log(data); //输出 haha
}, err=> {
    console.log(err);
})复制代码

上面能够注意到, new Promise中的改变状态操做咱们使用的是同步,那若是是异步呢,咱们平时遇到的基本都是异步操做,该如何解决?promise

这里咱们须要在构造函数中存放两个数组,分别保存成功回调和失败的回调
由于能够then屡次,因此须要将这些函数放在数组中
代码以下:bash

// 这里咱们建立了一个构造函数 参数就是执行器
function Promise(exector) {
    // 这里咱们将value 成功时候的值 reason失败时候的值放入属性中
    let self = this;
    // 这里咱们加入一个状态标识
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    // 存储then中成功的回调函数
    this.onResolvedCallbacks = [];
    // 存储then中失败的回调函数
    this.onRejectedCallbacks = [];
    
    // 成功执行
    function resolve(value) {
        // 判断是否处于pending状态
        if (self.status === 'pending') {
            self.value = value;
            // 这里咱们执行以后须要更改状态
            self.status = 'resolved';
            // 成功以后遍历then中成功的全部回调函数
            self.onResolvedCallbacks.forEach(fn => fn());
        }
    }
    
    // 失败执行
    function reject(reason) {
        // 判断是否处于pending状态
        if (self.status === 'pending') {
            self.reason = reason;
            // 这里咱们执行以后须要更改状态
            self.status = 'rejected';
            // 成功以后遍历then中失败的全部回调函数
            self.onRejectedCallbacks.forEach(fn => fn());
        }
    }
    
    // 这里对异常进行处理
     try {
        exector(resolve, reject);
    } catch(e) {
        reject(e)
    }
}


// then 改造

Promise.prototype.then = function(onFulfilled, onRejected) {
    // 获取下this
    let self = this;
    if (this.status === 'resolved') {
        onFulfulled(self.value);
    }
    
    if (this.status === 'rejected') {
        onRejected(self.reason);
    }
    
    // 若是异步执行则位pending状态
    if(this.status === 'pending') {
        // 保存回调函数
        this.onResolvedCallbacks.push(() => {
            onFulfilled(self.value);
        })

        this.onRejectedCallbacks.push(() => {
            onRejected(self.reason)
        });
    }
}


// 这里咱们能够再次实验

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        if(Math.random() > 0.5) {
            resolve('成功');
        } else {
            reject('失败');
        }
    })
})

promise.then((data) => {
    console.log('success' + data);
}, (err) => {
    console.log('err' + err);
})
复制代码

咱们都熟悉Jquery,它的链式调用是用rerun this来作的,但是这里却不行,缘由文章末尾再解释。 咱们采起返回一个新的promise对象来实现链式调用. 意思也就是p.then()返回一个新promise对象。dom

//给这个函数加个返回值,返回值就是一个新new的promise对象
Promise.prototype.then = function (onFulFilled, onRejected) {
    let p2 = new Promise((resolve, reject) => {});
    
    if (this.status === 'pending') {
        this.onFulFilledCallbacks.push(() => {
            onFulFilled(this.value)
        }); 
        this.onRejectedCallbacks.push(() => {
            onRejected(this.reason)
        })
    } else if (this.status === 'resolved') {
        onFulFilled(this.value);
    } else if (this.status === 'rejected') {
        onRejected(this.reason);
    }
    return p2;
}
复制代码

  • 加了两行代码,咱们测试一下输出结果:
    任务执行完了
    第一个成功回调
  • 根据上一次咱们所讲的能够总结出以下结论:真正触发onFulFilledCallbacks里所存储的回调函数只有两个地方:
  1. resolve被调用的时候.(resolve是用户调用的,由于用户固然知道哪一种逻辑算任务成功,哪一种逻辑算任务失败,例如咱们上一章的例子,随机数大于5我就认为是成功的.)
  2. then被调用的时候.(then也是用户调用的,promise里的任务执行完了后要作啥)

参考:

手写一个本身的Promise
异步

最最最通俗易懂的promise手写系列
函数

相关文章
相关标签/搜索