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与DOM组合动态创建表格实例
Dec 23 Javascript
利用js判断浏览器类型(是否为IE,Firefox,Opera浏览器)
Nov 22 Javascript
jQuery常用且重要方法汇总
Jul 13 Javascript
程序员必知35个jQuery 代码片段
Nov 05 Javascript
JS实用技巧小结(屏蔽错误、div滚动条设置、背景图片位置等)
Jun 16 Javascript
js 获取范围内的随机数实例代码
Aug 02 Javascript
Node.js编写CLI的实例详解
May 17 Javascript
vue增删改查的简单操作
Jul 15 Javascript
原生JS实现自定义滚动条效果
Oct 27 Javascript
js 发布订阅模式的实例讲解
Sep 10 Javascript
JS无限级导航菜单实现方法
Jan 05 Javascript
Vue封装的组件全局注册并引用
Jul 24 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
一条久听不愿放下的DIY森海MX500,三言两语话神奇
2021/03/02 无线电
PHP采集利器 Snoopy 试用心得
2011/07/03 PHP
PHP版本的选择5.2.17 5.3.27 5.3.28 5.4 5.5兼容性问题分析
2016/04/04 PHP
php表单处理操作
2017/11/16 PHP
php打开本地exe程序,js打开本地exe应用程序,并传递相关参数方法
2018/02/06 PHP
JavaScript 继承详解 第一篇
2009/08/30 Javascript
网易JS面试题与Javascript词法作用域说明
2010/11/09 Javascript
jquery的ajax请求全面了解
2013/03/20 Javascript
jquery实现动态画圆
2014/12/04 Javascript
transport.js和jquery冲突问题的解决方法
2015/02/10 Javascript
使用Node.js配合Nginx实现高负载网络
2015/06/28 Javascript
JS实现自动固定顶部的悬浮菜单栏效果
2015/09/16 Javascript
jQuery实现的跨容器无缝拖动效果代码
2016/06/21 Javascript
jQuery联动日历的实例解析
2016/12/02 Javascript
vue如何实现observer和watcher源码解析
2017/03/09 Javascript
微信小程序遇到修改数据后页面不渲染的问题解决
2017/03/09 Javascript
详解nodejs 开发企业微信第三方应用入门教程
2019/03/12 NodeJs
[49:35]KG vs SECRET 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/19 DOTA
Python使用SocketServer模块编写基本服务器程序的教程
2016/07/12 Python
Python数据分析库pandas基本操作方法
2018/04/08 Python
pytorch 指定gpu训练与多gpu并行训练示例
2019/12/31 Python
django迁移文件migrations的实现
2020/03/31 Python
html5 利用重力感应实现摇一摇换颜色可用来做抽奖等等
2014/05/07 HTML / CSS
基于HTML5陀螺仪实现ofo首页眼睛移动效果的示例
2017/07/31 HTML / CSS
海淘母婴商城:国际妈咪
2016/07/23 全球购物
Bluebella美国官网:英国性感内衣品牌
2018/10/04 全球购物
Etam德国:内衣精品店
2019/08/25 全球购物
经典优秀毕业生求职信范文分享
2013/12/18 职场文书
小孩百日宴答谢词
2014/01/15 职场文书
公司拓展活动方案
2014/02/13 职场文书
文明班级建设方案
2014/05/15 职场文书
2014年教师党员公开承诺书
2014/05/28 职场文书
产品质量保证书范本
2015/02/27 职场文书
单位考核鉴定意见
2015/06/05 职场文书
56句经典英文座右铭
2019/08/09 职场文书
pt-archiver 主键自增
2022/04/26 MySQL