如何利用ES6进行Promise封装总结


Posted in Javascript onFebruary 11, 2019

原生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.其他值

第一个then方法返回一个Promise对象,它的回调函数接受resFn和rejFN两个回调函数作为参数,把成功状态的处理封装为handle函数,接受成功的结果作为参数

在handle函数,根据onresolve返回值的不同做出不同的处理

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){
      return new Mypromise((resFN,rejFN)=>{
        if(this.status === 'fulfilled'){ // 当状态为成功时
          handle(this.s_res)
        }else if(this.status === 'rejected'){ // 当状态为失败时
          errBack(this.f_res)
        }else{ // ++ 状态没有改变
          this.query.push({ // 保存回调函数到队列中
            resolve:onresolve,
            reject:onreject
          })
        } 
        function handle(value){
          // 当then方法的onresolve方法有返回值时,保存其返回值,没有使用其保存的值
          let returnVal = onresolve instanceof Function && onresolve(value) || value
          // 如果onresolve方法返回的是promise对象,则调用其then方法
          if(returnVal&&returnVal['then'] instanceof Function){
            returnVal.then(res=>{
              resFN(res)
            },err=>{
              rejFN(err)
            })
          }else{
            resFN(returnVal)
          } 
        }
        function errBack(reason){
          if(onreject instanceof Function){
            let returnVal = reject(reason)
            if(typeof returnVal !== 'undenfined' && returnVal['then'] instanceof Function){
              returnVal.then(res=>{
                resFN(res)
              },err=>{
                rejFN(err)
              })
            }else{
              resFN(returnVal)
            }
          }else{
            rejFN(reason)
          }
        }
      })
    }
  }

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'

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
一个对于js this关键字的问题
Jan 09 Javascript
ExtJS 简介 让你知道extjs是什么
Dec 29 Javascript
基于JQuery框架的AJAX实例代码
Nov 03 Javascript
input输入框的自动匹配(原生代码)
Mar 19 Javascript
Js 代码中,ajax请求地址后加随机数防止浏览器缓存的原因
May 07 Javascript
jquery移动节点实例
Jan 14 Javascript
js实现遮罩层弹出框的方法
Jan 15 Javascript
详解JavaScript的闭包、IIFE、apply、函数与对象
Dec 21 Javascript
js实现可输入可选择的select下拉框
Dec 21 Javascript
使用javascript做在线算法编程
May 25 Javascript
原生JS实现简单的无缝自动轮播效果
Sep 26 Javascript
vue自定义键盘信息、监听数据变化的方法示例【基于vm.$watch】
Mar 16 Javascript
在vue项目中引入vue-beauty操作方法
Feb 11 #Javascript
Vue表单控件绑定图文详解
Feb 11 #Javascript
图文讲解vue的v-if使用方法
Feb 11 #Javascript
ES6 如何改变JS内置行为的代理与反射
Feb 11 #Javascript
ES6 更易于继承的类语法的使用
Feb 11 #Javascript
总结4个方面优化Vue项目
Feb 11 #Javascript
JavaScript 九种跨域方式实现原理
Feb 11 #Javascript
You might like
Smarty+QUICKFORM小小演示
2007/02/25 PHP
PHP print类函数使用总结
2010/06/25 PHP
php中并发读写文件冲突的解决方案
2013/10/25 PHP
php中session定期自动清理的方法
2015/11/12 PHP
PHP的swoole扩展安装方法详细教程
2016/05/18 PHP
js操作时间(年-月-日 时-分-秒 星期几)
2010/06/20 Javascript
js 纯数字不重复排列的另类方法
2010/07/17 Javascript
JavaScript之Getters和Setters 平台支持等详细介绍
2012/12/07 Javascript
详解JavaScript中数组的reduce方法
2016/12/02 Javascript
vue服务端渲染缓存应用详解
2018/09/12 Javascript
Vue Echarts实现可视化世界地图代码实例
2019/05/07 Javascript
微信小程序使用echarts获取数据并生成折线图
2019/10/16 Javascript
[01:00:53]OG vs IG 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
Windows系统配置python脚本开机启动的3种方法分享
2015/03/10 Python
python将图片文件转换成base64编码的方法
2015/03/14 Python
Python保存MongoDB上的文件到本地的方法
2016/03/16 Python
Python中执行存储过程及获取存储过程返回值的方法
2017/10/07 Python
Python实现的凯撒密码算法示例
2018/04/12 Python
pycharm 在windows上编辑代码用linux执行配置的方法
2018/10/27 Python
python 求10个数的平均数实例
2019/12/16 Python
Tensorflow实现在训练好的模型上进行测试
2020/01/20 Python
python GUI库图形界面开发之PyQt5时间控件QTimer详细使用方法与实例
2020/02/26 Python
python使用pyecharts库画地图数据可视化的实现
2020/03/25 Python
使用keras时input_shape的维度表示问题说明
2020/06/29 Python
Python自动化之UnitTest框架实战记录
2020/09/08 Python
关于python中remove的一些坑小结
2021/01/04 Python
美国单身专业人士在线约会网站:EliteSingles
2019/03/19 全球购物
戴尔马来西亚官网:Dell Malaysia
2020/05/02 全球购物
员工安全生产责任书
2014/07/22 职场文书
群众路线教育实践活动思想汇报(2014特荐篇)
2014/09/16 职场文书
毕业实习单位意见
2015/06/04 职场文书
企业管理不到位检讨书
2019/06/27 职场文书
创业计划书之旅游网站
2019/09/06 职场文书
Vue CLI中模式与环境变量的深入详解
2021/05/30 Vue.js
nginx 配置缓存
2022/05/11 Servers
windows server 2016 域环境搭建的方法步骤(图文)
2022/06/25 Servers