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 相关文章推荐
innerhtml用法 innertext用法 以及innerHTML与innertext的区别
Oct 26 Javascript
浅谈tudou土豆网首页图片延迟加载的效果
Jun 23 Javascript
利用浏览器全屏api实现js全屏
Jan 16 Javascript
jQuery CSS3相结合实现时钟插件
Jan 08 Javascript
Vue监听数组变化源码解析
Mar 09 Javascript
angularJs中datatable实现代码
Jun 03 Javascript
解决option标签selected="selected"属性失效的问题
Nov 06 Javascript
使用 Vue 绑定单个或多个 Class 名的实例代码
Jan 08 Javascript
jQuery判断自定义属性data-val用法示例
Jan 07 jQuery
ES6基础之默认参数值
Feb 21 Javascript
解决VUE-Router 同一页面第二次进入不刷新的问题
Jul 22 Javascript
vue监听滚动事件的方法
Dec 21 Vue.js
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
迁移PHP版本到PHP7
2015/02/06 PHP
PHP中大于2038年时间戳的问题处理方案
2015/03/03 PHP
php计算整个目录大小的方法
2015/06/01 PHP
PHP使用缓存即时输出内容(output buffering)的方法
2015/08/03 PHP
超强多功能php绿色集成环境详解
2017/01/25 PHP
PHP+JQUERY操作JSON实例
2017/03/23 PHP
JQuery 绑定select标签的onchange事件,弹出选择的值,并实现跳转、传参
2011/01/06 Javascript
关于JS控制代码暂停的实现方法分享
2012/10/11 Javascript
有关于JS构造函数的重载和工厂方法
2013/04/07 Javascript
jquery异步跨域访问代码
2013/06/28 Javascript
javascript事件冒泡详解和捕获、阻止方法
2014/04/12 Javascript
jquery中map函数遍历数组用法实例
2015/05/18 Javascript
Bootstrap+jfinal实现省市级联下拉菜单
2016/05/30 Javascript
JavaScript_ECMA5数组新特性详解
2016/06/12 Javascript
javaScript中定义类或对象的五种方式总结
2016/12/04 Javascript
vue实现简单表格组件实例详解
2017/04/16 Javascript
JS 60秒后重新发送验证码的实例讲解
2017/07/26 Javascript
解决vue 更改计算属性后select选中值不更改的问题
2018/03/02 Javascript
vue封装一个简单的div框选时间的组件的方法
2019/01/06 Javascript
vue组件之间通信方式实例总结【8种方式】
2019/02/22 Javascript
JS实现的杨辉三角【帕斯卡三角形】算法示例
2019/02/26 Javascript
小程序测试后台服务的方法(ngrok)
2019/03/08 Javascript
微信小程序可滑动周日历组件使用详解
2019/10/21 Javascript
[51:11]2014 DOTA2国际邀请赛中国区预选赛5.21 LGD-CDEC VS DT
2014/05/22 DOTA
[01:14]辉夜杯战队访谈宣传片—NEWBEE.Y
2015/12/26 DOTA
python中的yield使用方法
2014/02/11 Python
Python标准库之Sys模块使用详解
2015/05/23 Python
浅谈python字符串方法的简单使用
2016/07/18 Python
零基础使用Python读写处理Excel表格的方法
2019/05/02 Python
普天C++笔试题
2016/03/20 面试题
工作的心得体会
2013/12/31 职场文书
学校节水倡议书
2015/04/29 职场文书
小学庆六一主持词
2015/06/30 职场文书
职场中的你,辞职信写对了吗?
2019/06/26 职场文书
Python import模块的缓存问题解决方案
2021/06/02 Python
Java使用HttpClient实现文件下载
2022/08/14 Java/Android