promise由浅入深(附源码)

560 阅读7分钟

前言

相信大家对Promise一定不陌生,在项目开发中会经常使用到它。但它内部究竟是如何实现的?

1. Promise的基本使用

javaScript的一大特点就是单线程,为了不阻塞主进程的工作,
一些耗时的操作(如网络请求)一般都是放在异步队列中去异步执行。
但是这样也就产生了很大的问题,一些ajax请求依赖于上一个ajax请求的返回值,就出现了臭名昭著的地狱回调问题。
Promise就是为了解决这种现象应运而生的

Promise是一个构造函数,必须接收一个函数作为参数,这个函数我们一般称为executor,executor函数也接收两个参数,resolve、reject,这两个参数也是函数.

 let p1 = new Promise(function(resolve,reject){
     // 这里编写异步操作的代码
     setTimeout(function(){
         resolve(1)
     })
 })

这里,我们得到了p1这个对象,可以通过p1.then对拿到的结果进行处理 then函数接受两个函数类型的参数,第一个是onFulfilled(Promise内部的状态为fulfilled时,后续会讲到这个状态,这里暂且理解为成功拿到结果之后执行),第二个是onRejected(Promise内部的状态为rejected时,这里理解为结果错误的情况下执行)

    p1.then(function(data){
        console.log(data)
    },function(err){
        console.log(err)
    })

2. Promise的核心概念

Promise对象内部有三种状态

  • pending(进行中)

  • fulfilled(已成功)

  • rejected(已失败) promise的状态只由异步处理的结果来改变,外界无法改变它。初始状态为pending,异步操作成功之后执行了resolve函数,promise的状态由pending变为fulfilled,如果异步操作失败,则promise的状态由pending变为rejectedpromise的状态一经改变之后就不可更改

3. Promise简易实现

 学到现在,我们对promise的基本结构有了一定的了解,
 promise是一个构造函数,接收一个函数作为参数,内部有个状态标识,有两个可以改变状态的方法,
 resolve: 执行之后状态由pending变为fulfilled
 reject: 执行之后状态由pending变为rejected
 还有一个then方法,接收成功和失败之后的回调函数
//下面我们尝试自己写一个实现上述功能的promise吧~
// 定义Promise的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise{
    constructor(executor){
        if(typeof executor !== 'function'){
            throw new TypeError('executor must be a function!')
        }
        this.status = PENDING; //初始状态
        this.value = undefined;
        this.reason = undefined;
        
        const resolve = function(res){
            if(this.status === PENDING){
                this.status = FULFILLED;
                this.value = res;
              }
             }
                
        const reject = function(err){
            if(this.status === PENDING){
             this.status = REJECTED;
             this.reason = err
            }
           }
           //try catch用来捕获执行函数执行过程中报的错
           try{
               executor(resolve,reject)
           }catch(err){
           //如果有错误,就执行reject
               reject(err)
           }
    }
    then(onFulfilled,onReject){
        if(this.status === FULFILLED){
            onFulfilled(this.value)
        }
         if(this.status === REJECTED){
            onFulfilled(this.message)
        }
    }
}
上述代码带大家简单的了解了promise内部的执行结构以及状态变化,
但是并不支持异步和链式调用,我们接着更近一层吧~

4. Promise如何实现异步处理和链式调用

  • 异步 异步就是在执行promise.then()的时候,由于异步还没有执行完,所以,promise的状态还未改变,所以上面的代码只判断了status状态改变后执行就不行了,then里面的代码就不会执行了。而且then方法可以被同一个promise多次调用,我们可以创建一个回调的数组用来存放状态改变时需要执行的回调,在promise的状态改变时,再执行这些回调,到此为止,该Promise可以正常处理同步和异步的操作了
异步的处理是利用发布订阅的模式实现的,在then函数里,当promise是pending状态,收集(成功/失败)状态下需要执行的函数,放入成功或失败的回调数组里,当promise内部状态改变的时候,从根据当前的状态依次执行(成功/失败)回调数组里收集的函数需要注意:对同一个promise执行了多次then,那么then里面的回调函数要不都走的是成功的逻辑要不都走的是失败的逻辑!
    class Promise{
    constructor(executor){
        if(typeof executor !== 'function'){
            throw new TypeError('executor must be a function!')
        }
         this.status = PENDING; //初始状态
        this.value = undefined;
        this.reason = undefined;
        
        //onFulfilledCallbacks用来收集FULFILLED状态时要执行的回调函数
        this.onFulfilledCallbacks = [];
        
        //onRejectedCallbacks用来收集REJECTED状态时要执行的回调函数
        this.onRejectedCallbacks = [];
        
        const resolve = function(res){
            if(this.status === PENDING){
                this.status = FULFILLED;
                this.value = res;
            }
            let fn;
            // 一次执行队列中的函数,并清空队列 发布
            while(fn = this.onFulfilledCallbacks.shift()){
                fn();
            }
          }
        const reject = function(err){
            if(this.status === PENDING){
                 this.status = REJECTED;
                 this.reason = err
            }
             let fn;
            while(fn = this.onRejectedCallbacks.shift()){
                fn();
            }
        }
        }
        try{
               executor(resolve,reject)
           }catch(err){
               reject(err)
           }
    }
    then(onFulfilled,onReject){
        if(this.status === PENDING){
            //订阅
            this.onFulfilledCallbacks.push(()=>{
                onFulfilled(this.value)
            });
            this.onRejectedCallbacks.push(() => {
                onReject(this.reason)
            });
        }
        if(this.status === FULFILLED){
            onFulfilled(this.value)
        }
         if(this.status === REJECTED){
            onFulfilled(this.reason)
        }
    }
}
  • 链式调用

Promise的then方法时可以支持链式调用的,即promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2),所以then方法必须返回一个新的Promise对象,且promise的链式调用,必须保证在当前的promise的状态为fulfilled后,才会去执行下一个promise

       then(onFulfilled,onReject){
          // * 如果传入的回调不是函数的花,会有一个默认的回调,返回promise的值
          onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
          onReject = typeof onReject === 'function' ? onReject : (err) => err;
        // new Promise会立即执行executor,所以可以把then方法中的逻辑放入executor函数中
        let promise2 =  new Promise((resolve,reject) => {
            if( this.status === PENDING){
                //订阅
                this.onFulfilledCallbacks.push(()=>{
                    try {
                        resolvePromise( this.value,promise2, onFulfilled,resolve,reject)
                    } catch (error) {
                        reject(error)
                    }
                   
                });
                this.onRejectedCallbacks.push(() => {
                    try {
                     resolvePromise( this.reason,promise2,onReject,resolve,reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if( this.status === FULFILLED){
                setTimeout(() => {
                try {
                    resolvePromise(this.value,promise2, onFulfilled,resolve,reject)
                } catch (error) {
                    reject(error)
                }
                },0)
            }
            if(this.status === REJECTED){
                setTimeout(() => {
                try {
                    resolvePromise( this.reason,promise2,onReject,resolve,reject)
                } catch (error) {
                    reject(error)
                }
                },0)
            }
        })
        return promise2;
       
    }
    
    //辅助方法resolePromise
    function resolvePromise(value,promise2,callback,resolve,reject){
    // 执行回调,拿到回调的返回
    let x =  callback(value);
    if(x === promise2){
        return reject(new TypeError('TypeError')) 
    }
    // 用来解决promise的状态一旦发生改变,就不会再执行任何改变状态的操作了
    let called = false;
    if(typeof x === 'function' || (typeof x === 'object' && x !== null)){
        try {
            let then = x.then
            // 如果res有then方法,基本可以确定返回值是一个promise对象
            if(typeof then === 'function'){
                if(called) return true
                then.call(x, (y) => {
                    called = true
                    // resolve(y)
                    // 递归调用,解决resolve()传入的参数仍然是一个promise的问题
                    resolvePromise(y,promise2,(value) => value,resolve,reject)
                }, (r)=> {
                    called = true
                    reject(r)
                });
            }else {
                resolve(x)
            }
        } catch (error) {
            reject(error)
        }
     
    }else {
        resolve(x);
    }


}

学到现在,Promise的内部实现原理基本就很清晰了,then函数的链式调用实现原理有些复杂,核心就是then方法需要返回一个新的promise,且必须得保证上一个promise的状态改变之后才会执行下一个promise。了解promise的内部状态及其改变逻辑,会比较好理解一些~

Promise的其他API

  • catch catch是一种语法糖写法,本身执行的是then(undefined,(err)=>{}),由于Promise源码中的代码逻辑都在try catch中,所以一旦程序运行错误,都会执行错误监听的回调函数中。
catch(onError){
    this.then(undefined,onError)
}
  • finally finally是不管promise最终的状态如何,都会执行该方法。
    finally中的回调函数不接受任何的参数,说明finally函数的执行与状态无关
// 需要一个静态的resolve方法,返回一个Promise
static resolve(val){
       const p = new Promise(function(){});
       p.status = 'fulfilled';
       p.value = val;
       return p;
   }
finally(cb){
    return this.then(function(res){
        res => Promise.resolve(cd()).then(() => return res)
    },function(err){
        res => Promise.resolve(cd()).then(() => return err)
    })
}
  • 静态方法all Promise的all方法,用于将多个promise实例,封装成一个promise对象,当传入的所有promise实例的状态都改为fulfilled,当前的promise对象的状态才会变为fulfilled,当传入的promise实例中的其中一个状态变为rejected,当前promised对象的状态变为rejected,q且此时的promise对象的值为状态变为rejected的promise实例的值
    Promise.all = function(arr){
    return new MyPromise(function(resolve,reject){
        var args = [];
        var len = arr.length - 1;
        function handler(val,i){
            if(typeof val === 'function' || typeof val === 'object'){
                if(val instanceof MyPromise){
                    val.then(res => {
                        args[i] = res
                    },err => {
                        reject(err)
                    })
                }
            }
            args[i] = val;
            if(i === len){
                resolve(args)
            }
        }
        arr.forEach((item,i) => {
            handler(item,i)
        })
    })

}
  • 静态方法race race方法简单理解为就是赛跑,参数传入多个promise实例,哪个promise实例的状态先改变,返回的promise对象的状态就改变
    Promise.race = function(arr){
    return new MyPromise(function(resolve,reject){
        arr.forEach(item => {
            Promise.resolve(item).then(res => {
                resolve(res);
            },function(err){
                reject(err);
            })
        })
    })

总结

花了很长的时间,总结输出,过程中也自己也重新理解了不少。文章中有不足之处,还望大家指出赐教!

完结撒花🎉🎉🎉

src=http___www.p5w.net_kuaixun_hdshf_201804_W020180423551301258102.jpg&refer=http___www.p5w.jpg