如何利用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异或加解密效果代码
Jun 25 Javascript
jquery的extend和fn.extend的使用说明
Jan 09 Javascript
js 控制图片大小核心讲解
Oct 09 Javascript
TinyMCE汉化及本地上传图片功能实例详解
May 31 Javascript
JavaScript中的toString()和toLocaleString()方法的区别
Feb 15 Javascript
原JS实现banner图的常用功能
Jun 12 Javascript
vue中实现在外部调用methods的方法(推荐)
Feb 08 Javascript
通过seajs实现JavaScript的模块开发及按模块加载
Jun 06 Javascript
no-vnc和node.js实现web远程桌面的完整步骤
Aug 11 Javascript
JS模拟浏览器实现全局搜索功能
Sep 11 Javascript
《javascript设计模式》学习笔记三:Javascript面向对象程序设计单例模式原理与实现方法分析
Apr 07 Javascript
element el-table表格的二次封装实现(附表格高度自适应)
Jan 19 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
php 转换字符串编码 iconv与mb_convert_encoding的区别说明
2011/11/10 PHP
PHP __call()方法实现委托示例
2019/05/20 PHP
Laravel Reponse响应客户端示例详解
2020/09/03 PHP
javascript 检测浏览器类型和版本的代码
2009/09/15 Javascript
js 自定义个性下拉选择框示例
2013/08/20 Javascript
20条学习javascript的编程规范的建议
2014/11/28 Javascript
JavaScript弹出新窗口并控制窗口移动到指定位置的方法
2015/04/06 Javascript
浅谈javascript中for in 和 for each in的区别
2015/04/23 Javascript
Bootstrap中CSS的使用方法
2016/02/17 Javascript
jQuery实现div拖拽效果实例分析
2016/02/20 Javascript
js和jq使用submit方法无法提交表单的快速解决方法
2016/05/17 Javascript
js HTML5上传示例代码完整版
2016/10/10 Javascript
jquery 实现回车登录详解及实例代码
2016/10/23 Javascript
react router 4.0以上的路由应用详解
2017/09/21 Javascript
vue.js $refs和$emit 父子组件交互的方法
2017/12/20 Javascript
微信小程序模版渲染详解
2018/01/26 Javascript
NodeJS简单实现WebSocket功能示例
2018/02/10 NodeJs
React Native悬浮按钮组件的示例代码
2018/04/05 Javascript
jQuery实现form表单序列化转换为json对象功能示例
2018/05/23 jQuery
微信小程序自定义菜单切换栏tabbar组件代码实例
2019/12/30 Javascript
EXTJS7实现点击拖拉选择文本
2020/12/17 Javascript
分享Python字符串关键点
2015/12/13 Python
python自定义异常实例详解
2017/07/11 Python
Python定时器实例代码
2017/11/01 Python
Pytoch之torchvision.transforms图像变换实例
2019/12/30 Python
Python如何使用OS模块调用cmd
2020/02/27 Python
Python 判断时间是否在时间区间内的实例
2020/05/16 Python
PyCharm常用配置和常用插件(小结)
2021/02/06 Python
利物浦足球俱乐部官方网上商店:Liverpool FC Official Store
2018/01/13 全球购物
英国健康和美容技术产品购物网站:CurrentBody
2019/07/17 全球购物
编码转换,怎样实现将GB2312编码的字符串转换为ISO-8859-1编码的字符串
2014/01/07 面试题
销售自我评价
2013/10/22 职场文书
好习惯伴我成长演讲稿
2014/05/21 职场文书
2015年员工工作表现评语
2015/03/25 职场文书
西柏坡观后感
2015/06/08 职场文书
详解CocosCreator消息分发机制
2021/04/16 Javascript