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. jQuery中的closest()和parents()的差别
  2. *:教育产品 规范销售
  3. LeetCode 17. Letter Combinations of a Phone Number
  4. 色诱社报道:昨日,腾讯公司公布了2009年发展策划
  5. BZOJ 2588 Spoj 10628 Count on a tree | 树上主席树
  6. 【终结版】C#常用函数和方法集汇总
  7. 大数据技术周报第 003 期
  8. gmssl服务端和客户端程序、吉大正元身份认证网关、吉大正元SDK+USBkey 两两之间双证书双向认证数据通信测试
  9. 联想微型计算机u盘启动,联想ThinkCentre一体机如何设置U盘启动_联想ThinkCentre电脑怎样从USB启动...
  10. SOTIF-雨雪雾行驶场景下交通参与者视觉检测及识别
  11. nvidia-smi详解
  12. 华为防火墙安全区域介绍及配置
  13. 记云服务器中挖矿病毒与防范
  14. 在计算机术语中只读存储器常用,计算机应用基础复习题.doc
  15. 云堡垒机和信创堡垒机主要区别讲解
  16. php直播系统app吗,ThinkPHP完美运营版安卓苹果双端直播系统APP源码 带主播连麦PK功能源码...
  17. r语言中popsd和sd的区别_R语言中回归和分类模型选择的性能指标
  18. English语法_关系代词 - 定义与分类
  19. 0开始学py爬虫(学习笔记)
  20. 什么是高新技术企业?高新认定领域有哪些?

热门文章

  1. office365管理员删除用户邮箱
  2. 72分落户!2019年上海研究生户口标准公布!
  3. linux——sort uniq
  4. 代码随想录第二十五天|261.组合总和、17.电话号码的字母组合
  5. Property propDD has no initializer and is not definitely assigned in the constructor
  6. 页面停留时间和网站停留时间详解
  7. Github记录那些我的Interesting
  8. Android开发常见问题
  9. Android---给Linearlayout设置边框+弧度角
  10. springboot构建docker镜像并推送到阿里云