JavaScript基础教程之如何实现一个简单的promise


Posted in Javascript onSeptember 11, 2018

前言

我们在开发过程中大多会用到promise,想必大家对promise的使用都很熟练了,今天我们就来实现一个简单的promise,实现的效果如有出入还往指正。

Promise/A+规范:

  • 首先重新阅读了下A+的规范:
  • promise代表了一个异步操作的最终结果,主要是通过then方法来注册成功以及失败的情况,
  • Promise/A+历史上说是实现了Promise/A的行为并且考虑了一些不足之处,他并不关心如何创建,完成,拒绝Promise,只考虑提供一个可协作的then方法。

术语:

  • promise是一个拥有符合上面的特征的then方法的对象或者方法。
  • thenable是定义了then方法的对象或者方法
  • value是任何合法的js的值(包括undefined,thenable或者promise)
  • exception是一个被throw申明抛出的值
  • reason是一个指明了为什么promise被拒绝

整体结构

我们先来梳理一下整体的结果,以便后续好操作

class MyPromise {
 constructor(fn){
 
 }
 resolve(){

 }
 then(){

 }
 reject(){

 }
 catch(){

 }
}

Promise理论知识

摘抄至 http://es6.ruanyifeng.com/#docs/promise#Promise-all

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

总结一下就是promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),还有就是状态的改变只能是pending -> fulfilled 或者 pending -> rejected,这些很重要

实现构造函数

现在我们开始实现构造函数

class MyPromise {
 constructor(fn){
 if(typeof fn !== 'function') {
  throw new TypeError(`MyPromise fn ${fn} is not a function`)
 }
 this.state = 'pending';
 this.value = void 0;
 fn(this.resolve.bind(this),this.reject.bind(this))
 }
 ...
}

构造函数接收一个参数fn,且这个参数必须是一个函数,因为我们一般这样使用new Promise((resolve,reject)=>{});
然后初始化一下promise的状态,默认开始为pending,初始化value的值。

fn接收两个参数,resolve、reject

resolve

class MyPromise {
 constructor(fn){
 if(typeof fn !== 'function') {
  throw new TypeError(`MyPromise fn ${fn} is not a function`)
 }
 this.state = 'pending';
 this.value = void 0;
 fn(this.resolve.bind(this),this.reject.bind(this))
 }
 resolve(value){
 if(this.state !== 'pending') return;
 this.state = 'fulfilled';
 this.value = value
 }
 ...
}

当resolve执行,接收到一个值之后;状态就由 pending -> fulfilled;当前的值为接收的值

reject

class MyPromise {
 constructor(fn){
 if(typeof fn !== 'function') {
  throw new TypeError(`MyPromise fn ${fn} is not a function`)
 }
 this.state = 'pending';
 this.value = void 0;
 fn(this.resolve.bind(this),this.reject.bind(this))
 }
 resolve(value){
 if(this.state !== 'pending') return;
 this.state = 'fulfilled';
 this.value = value
 }
 reject(reason){
 if(this.state !== 'pending') return;
 this.state = 'rejected';
 this.value = reason
 }
}

当reject执行,接收到一个值之后;状态就由 pending -> rejected;当前的值为接收的值

then

class MyPromise {
 constructor(fn){
 if(typeof fn !== 'function') {
  throw new TypeError(`MyPromise fn ${fn} is not a function`)
 }
 this.state = 'pending';
 this.value = void 0;
 fn(this.resolve.bind(this),this.reject.bind(this))
 }
 resolve(value){
 if(this.state !== 'pending') return;
 this.state = 'fulfilled';
 this.value = value
 }
 reject(reason){
 if(this.state !== 'pending') return;
 this.state = 'rejected';
 this.value = reason
 }
 then(fulfilled,rejected){
 if (typeof fulfilled !== 'function' && typeof rejected !== 'function' ) {
  return this;
 }
 if (typeof fulfilled !== 'function' && this.state === 'fulfilled' ||
  typeof rejected !== 'function' && this.state === 'rejected') {
  return this;
 }
 return new MyPromise((resolve,reject)=>{
  if(fulfilled && typeof fulfilled === 'function' && this.state === 'fulfilled'){
  let result = fulfilled(this.value);
  if(result && typeof result.then === 'function'){
   return result.then(resolve,reject)
  }else{
   resolve(result)
  }
  }
  if(rejected && typeof rejected === 'function' && this.state === 'rejected'){
  let result = rejected(this.value);
  if(result && typeof result.then === 'function'){
   return result.then(resolve,reject)
  }else{
   resolve(result)
  }
  }
 })
 }
}

then的实现比较关键,首先有两个判断,第一个判断传的两个参数是否都是函数,如果部不是return this执行下一步操作。
第二个判断的作用是,比如,现在状态从pending -> rejected;但是中间代码中有许多个.then的操作,我们需要跳过这些操作执行.catch的代码。如下面的代码,执行结果只会打印1

new Promise((resolve,reject)=>{
 reject(1)
}).then(()=>{
 console.log(2)
}).then(()=>{
 console.log(3)
}).catch((e)=>{
 console.log(e)
})

我们继续,接下来看到的是返回了一个新的promise,真正then的实现的确都是返回一个promise实例。这里不多说

下面有两个判断,作用是判断是rejected还是fulfilled,首先看fulfilled,如果是fulfilled的话,首先执行fulfilled函数,并把当前的value值传过去,也就是下面这步操作,res就是传过去的value值,并执行了(res)=>{console.log(res)}这段代码;执行完成之后我们得到了result;也就是2这个结果,下面就是判断当前结果是否是一个promise实例了,也就是下面注释了的情况,现在我们直接执行resolve(result);

new Promise((resolve,reject)=>{
 resolve(1)
}).then((res)=>{
 console.log(res)
 return 2
 //return new Promise(resolve=>{})
})

剩下的就不多说了,可以debugger看看执行结果

catch

class MyPromise {
 ...
 catch(rejected){
  return this.then(null,rejected)
 }
}

完整代码

class MyPromise {
 constructor(fn){
  if(typeof fn !== 'function') {
   throw new TypeError(`MyPromise fn ${fn} is not a function`)
  }
  this.state = 'pending';
  this.value = void 0;
  fn(this.resolve.bind(this),this.reject.bind(this))
 }
 resolve(value){
  if(this.state !== 'pending') return;
  this.state = 'fulfilled';
  this.value = value
 }
 reject(reason){
  if(this.state !== 'pending') return;
  this.state = 'rejected';
  this.value = reason
 }
 then(fulfilled,rejected){
  if (typeof fulfilled !== 'function' && typeof rejected !== 'function' ) {
   return this;
  }
  if (typeof fulfilled !== 'function' && this.state === 'fulfilled' ||
   typeof rejected !== 'function' && this.state === 'rejected') {
   return this;
  }
  return new MyPromise((resolve,reject)=>{
   if(fulfilled && typeof fulfilled === 'function' && this.state === 'fulfilled'){
    let result = fulfilled(this.value);
    if(result && typeof result.then === 'function'){
     return result.then(resolve,reject)
    }else{
     resolve(result)
    }
   }
   if(rejected && typeof rejected === 'function' && this.state === 'rejected'){
    let result = rejected(this.value);
    if(result && typeof result.then === 'function'){
     return result.then(resolve,reject)
    }else{
     resolve(result)
    }
   }
  })
 }
 catch(rejected){
  return this.then(null,rejected)
 }
}

测试

new MyPromise((resolve,reject)=>{
 console.log(1);
 //reject(2)
 resolve(2)
 console.log(3)
 setTimeout(()=>{console.log(4)},0)
}).then(res=>{
 console.log(res)
 return new MyPromise((resolve,reject)=>{
 resolve(5)
 }).then(res=>{
 return res
 })
}).then(res=>{
 console.log(res)
}).catch(e=>{
 console.log('e',e)
})

执行结果:

> 1
> 3
> 2
> 5
> 4

原生promise

new Promise((resolve,reject)=>{
 console.log(1);
 //reject(2)
 resolve(2)
 console.log(3)
 setTimeout(()=>{console.log(4)},0)
}).then(res=>{
 console.log(res)
 return new Promise((resolve,reject)=>{
 resolve(5)
 }).then(res=>{
 return res
 })
}).then(res=>{
 console.log(res)
}).catch(e=>{
 console.log('e',e)
})

执行结果:

> 1
> 3
> 2
> 5
> 4

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
能说明你的Javascript技术很烂的五个原因分析
Oct 28 Javascript
js跨浏览器实现将字符串转化为xml对象的方法
Sep 25 Javascript
JS 获取鼠标左右键的键值方法
Oct 11 Javascript
jQuery实现类似老虎机滚动抽奖效果
Aug 06 Javascript
jquery实现下拉框功能效果【实例代码】
May 06 Javascript
jQuery实现订单提交页发送短信功能前端处理方法
Jul 04 Javascript
实现一个简单的vue无限加载指令方法
Jan 10 Javascript
JS验证不重复验证码
Feb 10 Javascript
vue 粒子特效的示例代码
Sep 19 Javascript
使用socket.io制做简易WEB聊天室
Jan 02 Javascript
手挽手带你学React之React-router4.x的使用
Feb 14 Javascript
node基于async/await对mysql进行封装
Jun 20 Javascript
Vue监听数据渲染DOM完以后执行某个函数详解
Sep 11 #Javascript
vue2.0$nextTick监听数据渲染完成之后的回调函数方法
Sep 11 #Javascript
Angular6 正则表达式允许输入部分中文字符
Sep 10 #Javascript
vue中使用input[type="file"]实现文件上传功能
Sep 10 #Javascript
详解Eslint 配置及规则说明
Sep 10 #Javascript
bootstrap自定义样式之bootstrap实现侧边导航栏功能
Sep 10 #Javascript
vue弹窗组件的实现示例代码
Sep 10 #Javascript
You might like
转生史莱姆:萌王第一次撸串开心到飞起,哥布塔撸串却神似界王神
2018/11/30 日漫
php微信公众平台交互与接口详解
2016/11/28 PHP
学习YUI.Ext 第四天--对话框Dialog的使用
2007/03/10 Javascript
jquery attr 设定src中含有&(宏)符号问题的解决方法
2011/07/26 Javascript
js获取客户端外网ip的简单实例
2013/11/21 Javascript
jQuery 滑动方法slideDown向下滑动元素
2014/01/16 Javascript
JQuery的Ajax请求实现局部刷新的简单实例
2014/02/11 Javascript
jQuery中事件对象e的事件冒泡用法示例介绍
2014/04/25 Javascript
javascript实现验证IP地址等相关信息代码
2015/05/10 Javascript
js实现超简单的展开、折叠目录代码
2015/08/28 Javascript
全面了解javascript三元运算符
2016/06/27 Javascript
javascript内存分配原理实例分析
2017/04/10 Javascript
JS移动端/H5同时选择多张图片上传并使用canvas压缩图片
2017/06/20 Javascript
Vue实例中生命周期created和mounted的区别详解
2017/08/25 Javascript
JavaScript封闭函数及常用内置对象示例
2019/05/13 Javascript
vue中监听返回键问题
2019/08/28 Javascript
[02:49]2014DOTA2电竞也是体育项目! 势要把荣誉带回中国!
2014/07/20 DOTA
在Python的Django框架中创建语言文件
2015/07/27 Python
Python面向对象程序设计示例小结
2019/01/30 Python
10招!看骨灰级Pythoner玩转Python的方法
2019/04/15 Python
python实现统计代码行数的小工具
2019/09/19 Python
Pycharm IDE的安装和使用教程详解
2020/04/30 Python
自定义Django_rest_framework_jwt登陆错误返回的解决
2020/10/18 Python
基于Python-turtle库绘制路飞的草帽骷髅旗、美国队长的盾牌、高达的源码
2021/02/18 Python
html5文本内容_动力节点Java学院整理
2017/07/11 HTML / CSS
iphoneX 适配客户端H5页面的方法教程
2017/12/08 HTML / CSS
canvas 基础之图像处理的使用
2020/04/10 HTML / CSS
资深财务管理人员自我评价
2013/09/22 职场文书
电气自动化自荐信
2013/10/10 职场文书
设计部经理的岗位职责
2013/11/16 职场文书
产品质量承诺书范文
2014/03/27 职场文书
结婚老公保证书
2015/02/26 职场文书
2015财务年终工作总结范文
2015/05/22 职场文书
2015年度高中教师工作总结
2015/05/26 职场文书
opencv 分类白天与夜景视频的方法
2021/06/05 Python
frg-100简单操作(设置)说明
2022/04/05 无线电