美文网首页
手写一个自己的Promise(1)

手写一个自己的Promise(1)

作者: d5fd51dd2b60 | 来源:发表于2018-07-24 23:34 被阅读0次

    这里我们先啰嗦一下Promise的概念:
    什么是promise

    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

    参考promiseA+规范总结:
    我们知道promise中共有三种状态
    pending 过渡态
    fulfilled 完成态
    rejected 失败态

    promise状态改变只有两种可能 过渡态=>成功态
    过渡态 => 失败态

    过程不可逆 无法相互转化

    这里来一发图片

    image
    let promise = new Promise((resolve, reject) => {
        //这里放入我们要执行的函数,可能是同步,也可能是异步, 这里我们就来写一个异步的执行
        setTimeout(() => {
            resolve('hello');
        })
    })
        
    promise.then(data => {
        console.log(data);
    }, err => {console.log(err)})
    

    上面代码表示我们new一个promise实例,并异步执行 这里通过调用then方法,我们成功得到了结果

    观察原生promise用法,我们可以发现,在new Promise时候传入了一个函数,这个函数在规范中的叫法是exector 执行器
    看到这里,我们先有一个大概思路,构建一个自己的Promise构造函数

    // 这里我们创建了一个构造函数 参数就是执行器
    function Promise(exector) {
        
    }
    

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

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

    // 这里我们创建了一个构造函数 参数就是执行器
    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)
        }
    }
    

    这里先留个小坑,一会我们回头来补上

    好了,Promise基本就是这样,是不是很简单,这里我们先实现一个简易版,后面的功能会逐步添加进去,不要心急,继续往后看

    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中的改变状态操作我们使用的是同步,那如果是异步呢,我们平时遇到的基本都是异步操作,该如何解决?

    这里我们需要在构造函数中存放两个数组,分别保存成功回调和失败的回调
    因为可以then多次,所以需要将这些函数放在数组中
    代码如下:

    // 这里我们创建了一个构造函数 参数就是执行器
    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);
    })
    
    

    这里要开始重点了,千万不要错过,通过以上代码,我们实现了一个简易版的promise,说简易版是因为我们的then方法只能调用一次,并没有实现原生promise中的链式调用。

    那链式调用是如何实现的呢?

    这里我们需要回顾下promiseA+规范,通过查看规范和阮一峰的es6讲解可以了解到

    then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

    这里我们看一段原生promise代码

    getJSON("/posts.json").then(function(json) {
      return json.post;
    }).then(function(post) {
      // ...
    });
    
    

    上面的代码使用then方法,依次指定了两个回调函数。
    第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

    采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

    后续更新中...

    相关文章

      网友评论

          本文标题:手写一个自己的Promise(1)

          本文链接:https://www.haomeiwen.com/subject/rtdumftx.html