8

原生Promise解析

简介
  • promise是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和强大。
  • promise简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,从语法上来说,Promise是一个对象,从它可以获取异步操作的消息,Promise提供统一的API,各种异步操作都可以用同样的方法进行处理
特点
  • 对象的状态不受外界影响,Promise对象代表一个异步操作,有三种状态:Pendding、fulfilled、rejected。只有异步操作的结果,可以决定当前是哪一种状态,其他操作都无法改变这个状态。
  • 一旦状态改变,就不会在变,任何时候都可以得到这个结果,只有两种可能:从Pendding变为fulfilled和从Pendding变为rejected。只要这两种情况发生,状态就凝固了,会一直保持这个结果,这时就称为resolved。

利用es6进行Promise封装

处理同步任务
  • 原生方法调用方式
    new Promise((resolve,reject)=>{
        resolve(1)
    }).then(res=>{
        console.log(res) //1
    })
  • 同步封装思考
    1.由调用方式可见Promise是一个类
    2.它接收一个回调函数,这个回调函数接受resolve和reject方法作为参数
    3.当状态改变后执行then方法,并将resolve或reject的结果作为then方法接受回调函数的参数
    class Mypromise{
        constructor(callback){
            this.status='pendding'
            //成功结果
            this.s_res = null
            // 失败结果
            this.f_res = null
            callback((arg)=>{ // 使用箭头函数this不会丢失
              // 改变状态为成功
              this.status = 'fulfilled'
              this.s_res = arg
            },(arg)=>{
               // 改变状态为失败
               this.status = 'rejected'
               this.f_res = arg 
            })
        }
        then(onresolve,onreject){
            if(this.status === 'fulfilled'){ // 当状态为成功时
                onresolve(this.s_res)
            }else if(this.status === 'rejected'){ // 当状态为失败时
                onreject(this.f_res)
            }
        }
    }
处理异步任务
  • 原生调用方式
    new Promise((resolve,reject)=>{
        setTimeOut(()=>{
            resolve(1)
        },1000)
    }).then(res=>{
        console.log(res)
    })
  • 异步封装思考
    1.根据js执行机制,setTimeOut属于宏任务,then回调函数属于微任务,当主线程执行完成后,会从异步队列中
    取出本次的微任务先执行。
    2.也就是说,then方法执行时,状态还没有改变,所有我们需要将then方法执行的回调保存起来,等到异步代码执行
    完成后,在统一执行then方法的回调函数
   class Mypromise{
        constructor(callback){
            this.status='pendding'
            //成功结果
            this.s_res = null
            // 失败结果
            this.f_res = null
            this.query = [] // ++  
            callback((arg)=>{ // 使用箭头函数this不会丢失
              // 改变状态为成功
              this.status = 'fulfilled'
              this.s_res = arg
              // 当状态改变后,统一执行then方法的回调
              this.query.forEach(item=>{
                  item.resolve(arg)
              })
            },(arg)=>{
               // 改变状态为失败
               this.status = 'rejected'
               this.f_res = arg 
               // 当状态改变后,统一执行then方法的回调
              this.query.forEach(item=>{
                  item.reject(arg)
              })
            })
        }
        then(onresolve,onreject){
            if(this.status === 'fulfilled'){ // 当状态为成功时
                onresolve(this.s_res)
            }else if(this.status === 'rejected'){ // 当状态为失败时
                onreject(this.f_res)
            }else{ // ++ 状态没有改变
                this.query.push({ // 保存回调函数到队列中
                    resolve:onresolve,
                    reject:onreject
                })
            }
        }
    } 
处理链式调用
  • 原生调用方式
    new Promise((resolve,reject)=>{
        resolve(1)
    }).then(res=>{
        return res
    }).then(res=>{
        console.log(res)
    })
  • 链式调用思考
    原生的Promise对象的then方法,返回的也是一个Promise对象,一个新的Promise才能支持链式调用
    下一个then方法可以接受上一个then方法的返回值作为回调函数的参数
    主要考虑上一个then方法的返回值:
        1.Promise对象/具有then方法的对象
        2.其他值
// 判断是否为函数
const isFn = (param)=>  typeof param === 'function';
// 判断是否为promise
const isPromise = (param)=> param instanceof Promise;
class MyPromise{
    constructor(callback) {
        this.status = 'pendding'; // 当前状态
        this._value; // 存储返回值
        this.query = []; // 存储任务队列
        this._resolve = this._resolve.bind(this);
        this._reject = this._reject.bind(this);
        callback(this._resolve,this._reject); // 重写回调函数
    }
    _resolve(val){
        if(this.status !== 'pendding') return;
        this.status = 'fulfilled';
        this._value = val;
        this.query.forEach(item=>{
            item.resolve(val);
        })
    }
    _reject(val){
        if(this.status !== 'pendding') return;
        this.status = 'rejected';
        this._value = val;
        this.query.forEach(item=>{
            item.reject(val);
        })
    }
    then(onResolve,OnReject){
        return new MyPromise((resolve,reject)=>{
            let renThen = (callback,resolver)=>{
                return function(val){ // 不是函数
                    try {
                        if(!isFn(callback)){
                            resolver(this.val);
                        }else{
                            const res = callback(val);
                            if(isPromise(res)){
                                res.then(resolve,reject);
                            }else{
                                resolver(res);
                            }
                        }
                    } catch (error) {
                        reject(error);
                    }
                }
            }
            if(this.status === 'fulfilled'){
                renThen(onResolve,resolve)(this._value);
            }else if(this.status === 'rejected'){
                renThen(OnReject,reject)(this._value);
            }else{
                this.query.push({
                    resolve: renThen(onResolve,resolve),
                    reject:renThen(OnReject,reject),
                })
            }
        })
    }
}
Promise.all和Promise.race方法
  • 原生调用方式
    Promise.all方法接受一个数组,数组中的每一项都是一个Promise实例,只有数组中的所有Promise实例的状态
    都变为fulfilled时,此时整个状态才会变成fulfilled,此时数组中所有Promise实例的返回值组成一个新的数组,
    进行传递。
    Promise.race方法和Promise.all方法一样,如果不是Promise实例,就会先调用Promise.resolve方法,将参数
    转为Promise实例,在进行下一步处理。
    只要数组中有一个参数的状态变为fulfilled就会进行传递
    // 将现有对象转换为Promise对象
    Mypromise.resolve = (arg)=>{
        if(typeof arg == 'undefined' || arg==null){ // 不带有任何参数
            return new Mypromise(resolve=>{
                resolve(arg)
            })
        }else if(arg instanceof Mypromise){ // 是一个Mypromise实例
            return arg
        }else if(arg['then'] instanceof Function){ // 具有then方法的对象
            return new Mypromise((resolve,reject)=>{
                arg.then(res=>{
                    resolve(res)
                },err=>{
                    reject(err)
                })
            })
        }else{ // 参数不是具有then方法的对象,或根本不是对象
           return new Mypromise(resolve=>{
                resolve(arg)
            }) 
        }
    }
    Mypromise.all = (arr)=>{
        if(!Array.isArray(arr)){
            throw new TypeError('参数必须是一个数组')
        }
        return new Mypromise((resolve,reject)=>{
            let i=0,result=[]
            next()
            functon next(){
                // 如果不是Mypromise实例需要转换
                Mypromise.resolve(arr[i]).then(res=>{
                    result.push(res)
                    i++
                    if(i===arr.length){
                        resolve(result)
                    }else{
                        next()
                    }
                },reject)
            }
        })
    }
    Mypromise.race = (arr)=>{
        if(!Array.isArray(arr)){
            throw new TypeError('参数必须是一个数组')
        }
        return new Mypromise((resolve,reject)=>{
            let done = false
            arr.forEach(item=>{
                Mypromise.resolve(item).then(res=>{
                    if(!done){
                        resolve(res)
                        done = true
                    }
                },err=>{
                    if(!done){
                        reject(res)
                        done = true
                    }
                })
            })
        })
    }
处理Mypromise状态确定不能改变的特性
    在重写callback中的resolve和reject方法执行前,先判断状态是否为'pendding'

cz160
154 声望3 粉丝

学习是一个终身的过程,前端技术更新迭代快,需要不断的去吸收新的技术,不要做被时代淘汰的人。