文档章节

深度解析利用ES6进行Promise封装总结

前端攻城小牛
 前端攻城小牛
发布于 02/14 15:06
字数 2022
阅读 450
收藏 9

这篇文章主要介绍了如何利用ES6进行Promise封装总结,文中通过示例代码介绍的非常详细,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。

原生Promise解析

简介

  • promise是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和强大。
  • promise简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,从语法上来说,Promise是一个对象,从它可以获取异步操作的消息,Promise提供统一的API,各种异步操作都可以用同样的方法进行处理

特点

对象的状态不受外界影响,Promise对象代表一个异步操作,有三种状态:Pendding、fulfilled、rejected。只有异步操作的结果,可以决定当前是哪一种状态,其他操作都无法改变这个状态。 一旦状态改变,就不会在变,任何时候都可以得到这个结果,只有两种可能:从Pendding变为fulfilled和从Pendding变为rejected。只要这两种情况发生,状态就凝固了,会一直保持这个结果,这时就称为resolved。 1.利用es6进行Promise封装

2.处理同步任务

3.原生方法调用方式

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)
    }//在此我向大家推荐一个前端全栈开发交流圈:619586920  突破技术瓶颈,提升思维能力
  }
}

处理异步任务

原生调用方式

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)
     })//在此我向大家推荐一个前端全栈开发交流圈:619586920  突破技术瓶颈,提升思维能力
    },(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
      })//在此我向大家推荐一个前端全栈开发交流圈:619586920  突破技术瓶颈,提升思维能力
    }
  }
} 

处理链式调用

原生调用方式

new Promise((resolve,reject)=>{
  resolve(1)
}).then(res=>{
  return res
}).then(res=>{
  console.log(res)
})//在此我向大家推荐一个前端全栈开发交流圈:619586920  突破技术瓶颈,提升思维能力

链式调用思考

原生的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
        })
      } //在此我向大家推荐一个前端全栈开发交流圈:619586920  突破技术瓶颈,提升思维能力
      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)
        })//在此我向大家推荐一个前端全栈开发交流圈:619586920  突破技术瓶颈,提升思维能力
      })
    }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)
      }
    })
  }//在此我向大家推荐一个前端全栈开发交流圈:619586920  突破技术瓶颈,提升思维能力
  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'

结语

感谢您的观看,如有不足之处,欢迎批评指正。

© 著作权归作者所有

共有 人打赏支持
前端攻城小牛
粉丝 32
博文 27
码字总数 47482
作品 0
浦东
私信 提问
加载中

评论(2)

hezhongjie
hezhongjie
你好,看完收获很多。有个地方有些自己的想法,不知道是不是:
1.then方法的链式调用的实现,可以直接返回this:
...
then (onresolve, onreject) {
if (this.status === 'fulfilled') {
onresolve && onresolve(this.s_res);
} else if (this.status === 'rejected') {
onreject && onreject(this.f_res);
} else {
this.query.push({
resolve: onresolve,
reject: onreject
})
}
return this;
}
...

2. Mypromise.all方法中,mypromise的触发应该是一次性全部发送的,而不是依次触发。
前端攻城老湿
前端攻城老湿
写得好
ES6-7

JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMAScript 6 Promises规范为中心,着重向各位读者介绍JavaScr...

掘金官方
2018/01/05
0
0
mpvue学习笔记-之微信小程序数据请求封装

简介 美团出品的mpvue已经开源出来很久了,一直说要进行一次实践,这不最近一次个人小程序开发就用上了它。 看了微信官方的数据请求模块--request,对比了下get和post请求的代码,发现如果在...

愿爱无忧dk_
2018/05/31
0
0
手写一个JSONP(promise封装)

前言 JSONP以前研究过,最近又有点忘了,写篇本文mark一下,旨在理解记住JSONP的原理及其实现。代码实现用到es6语法,使用promise来封装JSONP方法,本地测试用的自己node搭的服务器,具体代码...

TokenYang
2017/10/14
0
0
ES6 Promise 执行解析

作为一门单线程的语言,刚学习 JavaScript 语言的时候,我曾怀疑过 JavaScript 在处理 ajax 数据请求,文件解析等过程效率会很低,而且在执行这些任务较大的代码中,会严重阻塞后面代码的执行...

sandy_anqi
03/04
0
0
JavaScript 异步

JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过分页形式获取列表。...

掘金官方
2018/01/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

html5代码书写规范

DOCTYPE 页面文档类型统一使用HTML5 DOCTYPE. <!DOCTYPE html> Meta字符集设置 声明方法遵循HTML5的规范, Meta文件使用 "UTF-8" 浏览器显示编码指定. <meta charset="utf-8"> 手机端页面添......

niuhongxia
28分钟前
3
0
怎么修改 phpstorm 中注释的开始位置

PHPStorm 版本:v2018.3 如下图设置:

whoru
37分钟前
2
0
Android Arcface人脸识别sdk使用工具类

public class FaceUtil{ private static final String TAG = FaceUtil.class.getSimpleName(); private static FaceUtil faceInstance = null; public FaceDB mFaceDB; pri......

是哇兴哥棒棒哒
45分钟前
2
0
JFreeChart中文API和树形详解

-------------------------------- JfreeChart 中文API -------------------------------- 要想绘制出漂亮的图表,就必须了解图表的构成部分,将图表进行分解成N个部分。 然后再对每一个部分...

喜欢搬砖的农民工
47分钟前
2
0
Android ViewPager

1.PagerAdapter { public int getCount() { return list.size(); } public Object instantiateItem(ViewGroup container, int postion) { container.addView(iv); return iv; } public void ......

Coding缘
49分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部