如何利用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 相关文章推荐
使用javascript获取flash加载的百分比的实现代码
May 25 Javascript
js实现身份证号码验证的简单实例
Feb 19 Javascript
jQuery表单域选择器用法分析
Feb 10 Javascript
jquery实现鼠标滑过后动态图片提示效果实例
Aug 10 Javascript
JavaScript统计网站访问次数的实现代码
Nov 18 Javascript
jQuery实现点击后高亮背景固定显示的菜单效果【附demo源码下载】
Sep 21 Javascript
jQuery.cookie.js实现记录最近浏览过的商品功能示例
Jan 23 Javascript
jQuery表单验证之密码确认
May 22 jQuery
浅析为什么a="abc" 不等于 a=new String("abc")
Oct 25 Javascript
使用 Javascript 实现浏览器推送提醒功能的示例
Nov 03 Javascript
elementUI Tree 树形控件的官方使用文档
Apr 25 Javascript
vue.js watch经常失效的场景与解决方案
Jan 07 Vue.js
在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
咖啡豆的最常见发酵处理方法,详细了解一下
2021/03/03 冲泡冲煮
PHP命名空间(Namespace)简明教程
2014/06/11 PHP
11个PHPer必须要了解的编程规范
2014/09/22 PHP
Yii框架form表单用法实例
2014/12/04 PHP
php+mysqli批量查询多张表数据的方法
2015/01/29 PHP
使用PHPStorm+XDebug搭建单步调试环境
2017/11/19 PHP
双击滚屏-常用推荐
2006/11/29 Javascript
javascript下给元素添加事件的方法与代码
2007/08/13 Javascript
推荐一些非常不错的javascript学习资源站点
2007/08/29 Javascript
JQuery 常用方法基础教程
2009/02/06 Javascript
javascript中使用replaceAll()函数实现字符替换的方法
2010/12/25 Javascript
js返回前一页刷新本页重载页面
2014/07/29 Javascript
Eclipse配置Javascript开发环境图文教程
2015/01/29 Javascript
jQuery焦点图切换特效代码分享
2015/09/15 Javascript
js实现接收表单的值并将值拼在表单action后面的方法
2015/11/23 Javascript
前端分页功能的实现以及原理(jQuery)
2017/01/22 Javascript
微信小程序实现城市列表选择
2018/06/05 Javascript
VueJS 组件参数名命名与组件属性转化问题
2018/12/03 Javascript
JavaScript常用事件介绍
2019/01/21 Javascript
JS实现可控制的进度条
2020/03/25 Javascript
[05:39]2014DOTA2国际邀请赛 DK晋级胜者组专访战队国士无双
2014/07/14 DOTA
Python爬虫实现简单的爬取有道翻译功能示例
2018/07/13 Python
Python获取网段内ping通IP的方法
2019/01/31 Python
Python将列表数据写入文件(txt, csv,excel)
2019/04/03 Python
Python scrapy爬取小说代码案例详解
2020/07/09 Python
wordpress添加Html5的表单验证required方法小结
2020/08/18 HTML / CSS
加拿大花店:1800Flowers.ca
2016/11/16 全球购物
意大利香水和彩妆护肤品购物网站:Ditano
2017/08/13 全球购物
AJAX都有哪些有点和缺点
2012/11/03 面试题
国贸专业大学生职业生涯规划范文
2014/01/10 职场文书
12.4全国法制宣传日活动方案
2014/11/02 职场文书
法律进社区活动总结
2015/05/07 职场文书
如何在向量化NumPy数组上进行移动窗口
2021/05/18 Python
经典《舰娘》游改全新动画预告 预定11月开播
2022/04/01 日漫
MySQL 表锁定 LOCK和UNLOCK TABLES的 SQL语法
2022/04/18 MySQL