如何利用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 相关文章推荐
鼠标事件延时切换插件
Mar 12 Javascript
javascript生成随机数的方法
May 16 Javascript
JavaScript将字符串转换为整数的方法
Apr 14 Javascript
基于JavaScript实现移动端TAB触屏切换效果
Oct 20 Javascript
基于jQuery实现咖啡订单管理简单应用
Feb 10 Javascript
jQuery实现单击按钮遮罩弹出对话框效果(2)
Feb 20 Javascript
js封装成插件的步骤方法
Sep 11 Javascript
vue组件传递对象中实现单向绑定的示例
Feb 28 Javascript
Vue路由切换时的左滑和右滑效果示例
May 29 Javascript
jquery 验证用户名是否重复代码实例
May 14 jQuery
js实现图片跟随鼠标移动效果
Oct 16 Javascript
JS中作用域以及变量范围分析
Jul 18 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实现的观察者模式实例
2017/06/21 PHP
php实现每日签到功能
2018/11/29 PHP
PHP convert_cyr_string()函数讲解
2019/02/13 PHP
Laravel Eloquent ORM 多条件查询的例子
2019/10/10 PHP
让innerHTML的脚本也可以运行起来
2006/07/01 Javascript
fromCharCode和charCodeAt 方法
2006/12/27 Javascript
使用prototype.js进行异步操作
2007/02/07 Javascript
锋利的jQuery 要点归纳(一) jQuery选择器
2010/03/21 Javascript
图片上传判断及预览脚本的效果实例
2013/08/07 Javascript
js function定义函数的几种不错方法
2014/02/27 Javascript
jQuery插件Validation表单验证详解
2018/05/26 jQuery
webpack 最佳配置指北(推荐)
2020/01/07 Javascript
Python标准库之Sys模块使用详解
2015/05/23 Python
python数据结构之图深度优先和广度优先实例详解
2015/07/08 Python
Python IDLE 错误:IDLE''s subprocess didn''t make connection 的解决方案
2017/02/13 Python
在Python中使用AOP实现Redis缓存示例
2017/07/11 Python
基于tensorflow加载部分层的方法
2018/07/26 Python
django formset实现数据表的批量操作的示例代码
2019/12/06 Python
pytorch实现focal loss的两种方式小结
2020/01/02 Python
关于Python3爬虫利器Appium的安装步骤
2020/07/29 Python
python如何修改文件时间属性
2021/02/05 Python
css3.0新属性效果在ie下的解决方案
2010/05/10 HTML / CSS
HTML5 预加载让页面得以快速呈现
2013/08/13 HTML / CSS
GNC健安喜美国官网:美国第一营养品牌
2016/07/22 全球购物
trivago美国:全球最大的酒店价格比较网站
2018/01/18 全球购物
三八妇女节活动总结
2014/05/04 职场文书
2014年党课学习材料
2014/05/11 职场文书
论文诚信承诺书
2014/05/23 职场文书
中学生民族团结演讲稿
2014/08/27 职场文书
学习焦裕禄同志为人民服务思想汇报
2014/09/10 职场文书
2015年纪念“卢沟桥事变”78周年活动方案
2015/05/06 职场文书
运输公司工作总结
2015/08/11 职场文书
修改MySQL的默认密码的四种小方法
2021/05/26 MySQL
JUnit5常用注解的使用
2021/07/02 Java/Android
Python 详解通过Scrapy框架实现爬取百度新冠疫情数据流程
2021/11/11 Python
人民币符号
2022/02/17 杂记