Promise 是ES6异步编程的一种解决方案:
从语法上讲,promise是一个对象,从它可以获取异步操作的消息;
从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态:
pending(等待),
fulfiled(成功),
rejected(失败) ;
状态一旦改变,就不会再变。
创造promise实例后,它会立即执行。

让我们一起实现一款自己的Promise,顺便还可以理解理解源码。

Let’s go

首先打开我们的编辑器,新建一个.js格式的文件。声明PaddingFulfiedRejected三种状态,然后创建一个Class,当然你也可以使用构造函数写。
为了方便理解,每行代码都有注释。

//Promise/A+规定的三种状态
const Padding = 'Padding' //等待
const Fulfilled = 'Fulfilled' //成功
const Rejected = 'Rejected' //拒绝//创建一个名为 MyPromise 的类
class MyPromise {//写个构造器方便接受 new 的时候传进来的参数// 构造器    constructor(executor) {// 成功回调的队列this.resolveQueue = []        // 拒绝/失败的回调队列                    this.rejectQueue = []     // 储存当前队列的value       this.currentValue = null}
}

一、完善构造器

 // 构造器constructor(executor) {// 成功回调的队列this.resolveQueue = []// 拒绝/失败的回调队列this.rejectQueue = []// 储存当前队列的valuethis.currentValue = null// 判断executor是否是一个函数if (typeof executor !== 'function') {throw new Error('MyPromise is not a function')}// 修改状态为Paddingthis.status = Padding// 声明reslove 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数let _resolve = value => {/*把resolve执行回调的操作封装成一个函数,放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,我们还可以使用MutationObserver*/let run = () => {// 判断并修改状态if (this.modifyStatus(Fulfilled)) return// 储存当前的valuethis.currentValue = value// 判断成功队列是否存在值,存在则进行循环弹出,直到this.resolveQueue为空/*这里之所以使用一个队列来储存回调,是为了实现规范要求的< then 方法可以被同一个 promise 调用多次>如果使用一个变量而非队列来储存回调,那么即使多次 then()也只会执行一次回调*/while (this.resolveQueue.length) {// 弹出let callback = this.resolveQueue.shift()// 执行callback(value)}}// 调用setTimeout(run);}// 声明reject 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数let _reject = value => {/*把reject执行回调的操作封装成一个函数,放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,我们还可以使用MutationObserver*/let run = () => {// 判断并修改状态if (this.modifyStatus(Rejected)) return// 储存当前的valuethis.currentValue = value// 判断成功队列是否存在值,存在则进行循环弹出,直到this.rejectQueue为空/*这里之所以使用一个队列来储存回调,是为了实现规范要求的< catch 方法可以被同一个 promise 调用多次>如果使用一个变量而非队列来储存回调,那么即使多次 catch()也只会执行一次回调*/while (this.rejectQueue.length) {// 弹出let callback = this.rejectQueue.shift()// 执行callback(value)}}// 调用setTimeout(run)}// 执行executor(_resolve, _reject)}

1、 then

这个是promise的重要点,除了finally、reject不需要,其他的都需要用到他的原理内容。

 // resolve/then/成功then(resolvedFn, rejectedFn) {/*根据Promise A+规范,如果 then() 接收的参数不是function,那么我们应该忽略它。如果没有忽略,当then()回调不为function时将会抛出异常,导致链式调用中断*/// 判断resolvedFn是否是一个函数,不是函数则进行报错提醒if (resolvedFn && typeof resolvedFn !== 'function') {throw new Error('resolver is not a function')}// 判断rejectedFn是否是一个函数,不是函数则进行报错提醒if (rejectedFn && typeof rejectedFn !== 'function') {throw new Error('rejecter is not a function')}// 为了实现.then的链式调用,此时需要返回一个promisereturn new MyPromise((resolve, reject) => {/*then*///把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论let fulfilledFn = value => {try {//执行第一个(当前的)Promise的成功回调,并获取返回值let x = resolvedFn(value)//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)} catch (error) {reject(error)}}// 设置对列this.resolveQueue.push(fulfilledFn)/*reject根据设置resolvedFn的原理,设置一下rejectFn*///把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论let rejectFn = value => {try {//执行第一个(当前的)Promise的成功回调,并获取返回值let x = rejectedFn(value)//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)} catch (error) {reject(error)}}// 设置对列this.rejectQueue.push(rejectFn)/*处理状态为resolve/reject的情况:上边的写法是对应状态为padding的情况,有些时候resolve/reject 在 then() 之前就被执行(比如Promise.resolve().then()),如果这个时候还把then()回调push进resolve/reject的执行队列里,那么回调将不会被执行,因此对于状态已经变为fulfilled或rejected的情况,我们直接执行then回调*/// 当前状态判断switch (this._status) {// 当状态为pending时,把resolve和reject回调push进resolve/reject执行队列,等待执行case Padding:this.resolveQueue.push(fulfilledFn)this.rejectQueue.push(rejectFn)break;// 当状态已经变为resolve/reject时,直接执行then回调case Fulfilled:fulfilledFn(this.currentValue)break;case Rejected:rejectFn(this.currentValue)break;}})}

2、 catch

 // reject/catch 失败catch (rejectedFn) {// 判断是否是一个函数if (typeof rejectedFn === 'function') {return this.then(null, rejectedFn)}}

3、 resolve

 // resolve /*resolve(resolver)方法返回一个以给定值解析后的Promise 对象。如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法)),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。*/static resolve(resolver) {// 根据规范, 如果参数是Promise实例, 直接return这个实例if (resolver instanceof MyPromise) return resolverreturn new MyPromise(resolve => resolve(resolver))}

4、 reject

 // reject/* reject()方法返回一个带有拒绝原因的Promise对象。 */static reject(reason) {return new MyPromise((resolve, reject) => reject(reason))}

5、 finally

finally 无论成功失败都会返回,该方法与状态无关

 // finally 无论成功失败都会返回-该方法与状态无关finally(finallyFn) {return this.then(// resolve执行回调,并在then中return结果传递给后面的Promisevalue => MyPromise.resolve(callback()).then(() => value),// resolve执行回调,并在then中return结果传递给后面的Promisereason => MyPromise.resolve(callback()).then(() => {throw reason}))}

6、 all

Promise.all方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成**(resolved)**
或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),
此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

 /*Promise.all(iterable)方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中  promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。*/static all(promiseArr) {let index = 0let result = []return new MyPromise((resolve, reject) => {promiseArr.forEach((item, itemIndex) => {//resolve(item)用于处理传入的值不为Promise的情况MyPromise.resolve(item).then(val => {index++result[itemIndex] = value//所有then执行后, resolve结果if (index === promiseArr.length) {resolve(result)}},//有一个Promise被reject时,MyPromise的状态变为rejecterr => reject(err))})})}

7、 race

race方法返回一个 promise
一旦迭代器中的某个promise解决或拒绝,返回的 promise 状态为解决或拒绝。

 /*race(iterable)方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise 状态为解决或拒绝。*/static race(promiseArr) {return new MyPromise((resolve, reject) => {//同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态for (let item of promiseArr) {MyPromise.resolve(item).then( //resolve(item)用于处理传入值不为Promise的情况value => resolve(value),err => reject(err))}})}

二、 关于修改状态

我这里是把修改状态单独拿出来的,代码如下:

 // 修改状态modifyStatus(status) {/* 因为状态修改过后不可重新修改,所以我们要对当前状态进行判断,不是padding状态直接return出去这里return 为true是因为上面通过这个做过状态校验判断*/if (this.status !== Padding) return true// 修改状态this.status = status}

三、 完整代码

不想复制阅读的同学可以点击 -> Promise 原理/实现自己的Promise
进行下载。使用的时候就像我们日常使用Promise那样使用就OK了,只是我们需要导入这个js

//Promise/A+规定的三种状态
const Padding = 'Padding' //等待
const Fulfilled = 'Fulfilled' //成功
const Rejected = 'Rejected' //拒绝// 声明类
class MyPromise {// 构造器constructor(executor) {// 成功回调的队列this.resolveQueue = []// 拒绝/失败的回调队列this.rejectQueue = []// 储存当前队列的valuethis.currentValue = null// 判断executor是否是一个函数if (typeof executor !== 'function') {throw new Error('MyPromise is not a function')}// 修改状态为Paddingthis.status = Padding// 声明reslove 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数let _resolve = value => {/*把resolve执行回调的操作封装成一个函数,放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,我们还可以使用MutationObserver*/let run = () => {// 判断并修改状态if (this.modifyStatus(Fulfilled)) return// 储存当前的valuethis.currentValue = value// 判断成功队列是否存在值,存在则进行循环弹出,直到this.resolveQueue为空/*这里之所以使用一个队列来储存回调,是为了实现规范要求的< then 方法可以被同一个 promise 调用多次>如果使用一个变量而非队列来储存回调,那么即使多次 then()也只会执行一次回调*/while (this.resolveQueue.length) {// 弹出let callback = this.resolveQueue.shift()// 执行callback(value)}}// 调用setTimeout(run);}// 声明reject 因为箭头函数没有自己的this, 所以这里我们需要使用箭头函数let _reject = value => {/*把reject执行回调的操作封装成一个函数,放进setTimeout里,以兼容executor是同步代码的情况,除了可以使用setTimeout,我们还可以使用MutationObserver*/let run = () => {// 判断并修改状态if (this.modifyStatus(Rejected)) return// 储存当前的valuethis.currentValue = value// 判断成功队列是否存在值,存在则进行循环弹出,直到this.rejectQueue为空/*这里之所以使用一个队列来储存回调,是为了实现规范要求的< catch 方法可以被同一个 promise 调用多次>如果使用一个变量而非队列来储存回调,那么即使多次 catch()也只会执行一次回调*/while (this.rejectQueue.length) {// 弹出let callback = this.rejectQueue.shift()// 执行callback(value)}}// 调用setTimeout(run)}// 执行executor(_resolve, _reject)}// resolve/then/成功then(resolvedFn, rejectedFn) {/*根据规范,如果 then() 接收的参数不是function,那么我们应该忽略它。如果没有忽略,当then()回调不为function时将会抛出异常,导致链式调用中断*/// 判断resolvedFn是否是一个函数,不是函数则进行报错提醒if (resolvedFn && typeof resolvedFn !== 'function') {throw new Error('resolver is not a function')}// 判断rejectedFn是否是一个函数,不是函数则进行报错提醒if (rejectedFn && typeof rejectedFn !== 'function') {throw new Error('rejecter is not a function')}// 为了实现.then的链式调用,此时需要返回一个promisereturn new MyPromise((resolve, reject) => {/*then*///把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论let fulfilledFn = value => {try {//执行第一个(当前的)Promise的成功回调,并获取返回值let x = resolvedFn(value)//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)} catch (error) {reject(error)}}// 设置对列this.resolveQueue.push(fulfilledFn)/*reject根据设置resolvedFn的原理,设置一下rejectFn*///把resolvedFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论let rejectFn = value => {try {//执行第一个(当前的)Promise的成功回调,并获取返回值let x = rejectedFn(value)//分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve//这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)} catch (error) {reject(error)}}// 设置对列this.rejectQueue.push(rejectFn)/*处理状态为resolve/reject的情况:上边的写法是对应状态为padding的情况,有些时候resolve/reject 在 then() 之前就被执行(比如Promise.resolve().then()),如果这个时候还把then()回调push进resolve/reject的执行队列里,那么回调将不会被执行,因此对于状态已经变为fulfilled或rejected的情况,我们直接执行then回调*/// 当前状态判断switch (this._status) {// 当状态为pending时,把resolve和reject回调push进resolve/reject执行队列,等待执行case Padding:this.resolveQueue.push(fulfilledFn)this.rejectQueue.push(rejectFn)break;// 当状态已经变为resolve/reject时,直接执行then回调case Fulfilled:fulfilledFn(this.currentValue)break;case Rejected:rejectFn(this.currentValue)break;}})}// reject/catch 失败catch (rejectedFn) {// 判断是否是一个函数if (typeof rejectedFn === 'function') {return this.then(null, rejectedFn)}}// finally 无论成功失败都会返回-该方法与状态无关finally(finallyFn) {return this.then(// resolve执行回调,并在then中return结果传递给后面的Promisevalue => MyPromise.resolve(callback()).then(() => value),// resolve执行回调,并在then中return结果传递给后面的Promisereason => MyPromise.resolve(callback()).then(() => {throw reason}))}// resolve /*resolve(resolver)方法返回一个以给定值解析后的Promise 对象。如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法)),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。*/static resolve(resolver) {// 根据规范, 如果参数是Promise实例, 直接return这个实例if (resolver instanceof MyPromise) return resolverreturn new MyPromise(resolve => resolve(resolver))}// reject/* reject()方法返回一个带有拒绝原因的Promise对象。 */static reject(reason) {return new MyPromise((resolve, reject) => reject(reason))}// all/*Promise.all(iterable)方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中  promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。*/static all(promiseArr) {let index = 0let result = []return new MyPromise((resolve, reject) => {promiseArr.forEach((item, itemIndex) => {//resolve(item)用于处理传入的值不为Promise的情况MyPromise.resolve(item).then(val => {index++result[itemIndex] = value//所有then执行后, resolve结果if (index === promiseArr.length) {resolve(result)}},//有一个Promise被reject时,MyPromise的状态变为rejecterr => reject(err))})})}// race/*race(iterable)方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise 状态为解决或拒绝。*/static race(promiseArr) {return new MyPromise((resolve, reject) => {//同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态for (let item of promiseArr) {MyPromise.resolve(item).then( //resolve(item)用于处理传入值不为Promise的情况value => resolve(value),err => reject(err))}})}// 修改状态modifyStatus(status) {/* 因为状态修改过后不可重新修改,所以我们要对当前状态进行判断,不是padding状态直接return出去这里return 为true是因为上面通过这个做过状态校验判断*/if (this.status !== Padding) return true// 修改状态this.status = status}
}

Promise详解-手写Promise,实现一款自己的简易Promise相关推荐

  1. 深入讲解音视频编码原理,H264码流详解——手写H264编码器

    音视频高手课08-H264 I帧 P帧 B帧及手写H264编码器 1 三种帧的说明 1.I 帧:帧内编码帧,帧表示关键帧,你可以理解为这一帧画面的完整保留:解码时只需要本帧数据就可以完成(因为包含完整 ...

  2. JS 的 Promise详解

    @[TOC](JS 的 Promise详解)欧诺个鱼 1.概念 ES 6 开始支持 Promise. Promise 对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示.简而言之,就是处理 ...

  3. angular $q promise详解

    前言 通过本文,你大概能清楚angular promise是个啥,$q又是个啥,以及怎么用它.这里咱们先灌输下promise的思想. 下面写的全是废话,一些看着高逼格其实没什么大作用的概念,想知道$q ...

  4. promise 详解

    Promise 作用:主要是来解决Ajax的异步问题 传统方式:回调函数来解决异步问题,类似如下问题 ajax(url, {// 成功后回调ajax(url, {// 成功之后再回调ajax(url, ...

  5. 【Promise】自定义 - 手写Promise - Promise.all - Promise(executor)

    手写Promise 1. 整体结构框架 2. Promise(executor) 3. Promise.prototype.then 4. Promise.prototype.catch 5. Pro ...

  6. 手写一个promise用法_手写一个 Promise

    1 js 的基本数据类型? 2 JavaScript 有几种类型的值? 3 什么是堆?什么是栈?它们之间有什么区别和联系? 4 内部属性 [Class] 是什么? 5 介绍 js 有哪些内置对象? 6 ...

  7. Promise使用,promise原理,手写promise

    Js手写Promise 前言 作为一名合格的前端写手,手写一些常见的方法是不可或缺的一项技能. 手写Promise 涉及到高阶函数,在案例中我也会讲到,从一个小白的视角告诉各位职友 一.Promise ...

  8. vue 手写签名_手写Promise/Promise.all/Promise.race(手写系列一)

    背景 几个月没写文章了,愧对关注本专栏的小伙伴.最近有同学提议我出一个手写系列的文章对常见对前端工具.框架.设计模式做一个覆盖.同时有个要求:代码要尽量短小易懂,并且体现原理,让学习者学习过后能在未来 ...

  9. Promise原理及手写Promise

    原理: Promise 原理围绕以下三个问题进行解决: (有任何一步不了解的话请先往下看手写Promise,会一步步剖析原理,看完后再继续回顾这里!!) 1. 怎么实现异步? Promise内部the ...

最新文章

  1. 七天快排优化真的存在!
  2. Solaris的syslog机制
  3. 多线程-NSOperation
  4. AngularJS快速入门指南03:表达式
  5. VS2019注释整段代码
  6. 微服务 注册中心_4.微服务架构的第二个组件:注册中心
  7. mysql deadlock 记录_MySQL DeadLock故障排查全过程记录
  8. 从复杂指令系统到精简指令系统
  9. 4.Linux/Unix 系统编程手册(上) -- 文件IO:通用的IO模型
  10. python确定指标权重_python 实现熵权法确定各指标的权重
  11. matlab读取2级文件夹,并把图像保存到指定的文件夹
  12. 【情感识别】基于matlab支持向量机(SVM)语音情感识别【含Matlab源码 543期】
  13. xss漏洞java代码_Java处理XSS漏洞的工具类代码
  14. xlsread的返回值
  15. add_days oracle_Oracle日期函数简介
  16. vue动态切换背景图片background
  17. c++ 消息分发 消息管理
  18. 从键盘上输入一个字符,如果它是小写字母,则把它转换成大写字母输出;否则,直接输出。
  19. 【camera】【ISP】Lens Shading Correction镜头阴影校正
  20. 深入理解并行编程原理与实践

热门文章

  1. 我曾七次鄙视我的灵魂--纪伯伦
  2. strongswan libcharon
  3. 代码随想录算法训练营day25| 216.组合总和III、17.电话号码的字母组合
  4. 宝塔面板安装软件或扩展一直“等待安装”状态(sleeping)的处理
  5. 物流技术界春晚又开始了-CeMat
  6. python 应声虫怎么做_生活中要敢于表达自己的想法,永远不做“应声虫”!
  7. Docker 网络模型/overlay 跨主机网络
  8. VL6180X传感器驱动
  9. 为什么计算机时间要从1970年1月1日开始算起?
  10. U盘复制文件到最后5秒会卡住怎么办解决