源码

very-simple-promise,包含了不完善的单元测试❤️.

感谢

代码君的自白

这篇的文章主要参考了上面的博客,谢谢他的帮助?。

Promise/A+规范

Promises/A+规范, 下面?是对规范的内容的部分翻译。英文比较烂,不喜欢的可以不看

承诺必须满足三种状态, pending(等处理), fulfilled(履行), rejected(拒绝)

Promises的状态

  • promise处于pending时
  • 可以过渡到fulfilled或者rejected状态
  • promise处于fulfilled时
  • 不能向其他状态过渡
  • promise处于rejected时
  • 不能向其他状态过渡

then

promise的then方法接受两个参数

promise.then(onFulfilled, onRejected)
复制代码
  • onFulfilled和onRejected都是可选参数

    • onFulfilled不是函数需要忽略
    • onRejected不是函数需要忽略
  • onFulfilled如果是一个函数
    • onFulfilled在promise状态为fulfilled时被调用
    • onFulfilled只能被调用一次
    • onFulfilled不能在fulfilled之前不能调用
  • onRejected如果是一个函数
    • onRejected在promise状态为rejected时被调用
    • 在rejected之前不能调用它
    • onRejected只能被调用一次
  • onFulfilled or onRejected must not be called until the execution context stack contains only platform code. (原文不太懂什么意思,但是规范给出了注解)
  • onFulfilled和onRejected必须作为函数调用
  • then可以在同一个promise多次调用
    • 如果promise被履行, 则onFulfilled回调必须按照then的顺序执行
    • 如果promise被拒绝, 则onRejected回调必须按照then的顺序执行
promise2 = promise1.then(onFulfilled, onRejected);
复制代码
  • then必须返回promise

    • 如果onFulfilled或者onRejected返回一个值,则运行处理Promise的过程[[Resolve]](promise2, x)(这里处理的Resolve(promise2, x), onFulfilled或者onRejected的返回值会被resolve处理, resolve的第一个参数是then返回的新的promise)
    • 如果onFulfilled, onRejected抛出错误error, 则promise2必须为rejected
    • 如果onFulfilled不是一个函数, 而promise1的状态为fulfilled, promise2必须与promise1具有相同的值被履行
    • 如果onRejected不是一个函数, 而promise1的状态为rejected, promise2必须与promise1具有相同的值被拒绝

处理Promise

[[Resolve]](promise, x)需要遵循以下规范

  • 如果promise与x相等, 抛出TypeError的错误, 并以错误为理由拒绝promise
  • 如果x是Promise
    • 如果x处于pending,promise也需要保持pending,直到x接受pending
    • 如果x处于fulfilled,promise需要使用相同值执行promise
    • 如果x处于rejected,promise需要使用相同的值拒绝promise
  • 如果x是对象或者函数
    • 将x.then赋于then
    • 如果获取x.then时抛出错误,则使用错误作为理由拒绝promise
    • 如果then是函数?️
      • 使用x作为then函数的作用域中的this, 传入两个参数作为回调,分别是resolvePromise, 和rejectPromise
      • 如果resolvePromise如果以y为参数运行,则运行[[Resolve]](promise, y), 履行promise。resolve(promise,y)
      • 如果rejectPromise以r为参数运行,则以r为原因拒绝promise。reject(promise, r)
      • 如果then抛出了错误
        • 如果resolvePromise,rejectPromise已经调用对错误忽略
        • 如果没有调用,用错误作为原因拒绝promise
    • 如果then不是函数,使用x作为参数执行promise
  • 如果x不是对象也不是函数,则使用x作为参数执行promise

注解

onFulfilled和onRejected方法应当是异步执行,且应该在then方法被调用的那一轮事件循环之后的微任务执行栈中执行。可以使用宏任务macrotask和微任务microtask机制实现。也就是说宏任务完成后(Promise的then调用后),会清空微任务队列(执行onFulfilled或者onRejected)。

macrotask包含:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage 、requestAnimationFrame 、MessageChannel、etImmediate(Node.js 环境)

microtask包含:Promise.then、setImmediate、MutaionObserver、process.nextTick(Node.js 环境)

事件循环

下面?是一个关于事件循环的例子?

Promise中的参数函数,应当是立即调用的。我们这时先执行了resolve(1), 这会将外层的Promise的状态置为fulfilled态。但是这时外层的then还没有执行。

我们接下来执行了Promise.resolve().then(() => console.log(2)), 我们将then的onFulfilled,push到了macrotask队列中,会在宏任务执行完成后清空微任务。

执行外层Promise的then, onFulfilled, push到了macrotask队列中。

接着执行console.log(3), 然后清空macrotask队列,执行2,1(队列先进先出)

Promise构造函数


const pending = 0
const fulfilled = 1
const rejected = 2function reject (promise, result) {}function resolve (promise, reason) {}export default class Promise {constructor (fn) {this.fn = fn// Promise的状态, 初始状态为pendingthis._state = pending// Promise的fulfilled态的原因,或者Promise的rejected态的理由this._value = null// 存储then的队列this._tasks = []// 创建Promise对象后,立即执行fnthis.init()}init () {try {// 执行fn, 传入包装后的resolve,reject// reject,resolve会修改promise的状态this.fn((result) => resolve(this, result),(reason) => reject(this, reason))} catch (error) {reject(this)}}
}
复制代码

根据规范2.2.6: then may be called multiple times on the same promisethen可以在同一个promise中多次调用。所以我们用一个数组存储then的结果。

Promise.prototype.then

根据规范2.2.1: A promise’s then method accepts two arguments. Both onFulfilled and onRejected are optional arguments。then接受两个参数, 两个参数可选。

根据规范2.2.1.1, 2.2.1.2: If onFulfilled is not a function, it must be ignored. If onRejected is not a function, it must be ignored.。onFulfilled, onRejected必须是函数负责会被忽略。

根据规范2.2.2, 2.2.3, onFulfilled必须在状态为fulfilled调用, onRejected必须在状态为rejected调用

根据规范2.2.7: then must return a promise。then必须返回一个promise。

?️ 为什么不能返回当前的promise?因为根据规范2.1.2, 2.1.3, promise被修改状态后,不能再次修改状态。我们如果需要执行then的callback需要修改promise的状态。

我们使用task封装?then的回调以及then返回的promise。


class Task {constructor (onFulfilled, onRejected, promise) {if (typeof onFulfilled !== 'function') {onFulfilled = null}if (typeof onRejected !== 'function') {onRejected = null}this.onFulfilled = onFulfilledthis.onRejected = onRejectedthis.promise = promise}
}class Promise {// ...then (onFulfilled, onRejected) {let nextPromise = new Promise(function () {})let task = new Task(onFulfilled, onRejected, nextPromise)if (this._state === pending) {this._tasks.push(task)} else {handlePromise(this, task)}// 返回新的promisereturn nextPromise}
}复制代码

Promise.prototype.catch

catch函数同样会返回新的promise, 但是在创建task时,我们不会传入onFulfilled参数。所以当promise当为fulfilled态,虽然catch的回调同样存放_task中,但是由于callback为null, 在handlePromise中会向下穿透。

class Promise {// ...catch (onRejected) {let nextPromise = new Promise(function () {})// onFulfilled设置为nulllet task = new Task(null, onRejected, nextPromise)if (this._state === pending) {this._tasks.push(task)} else {handlePromise(this, task)}// 返回新的promisereturn nextPromise}
}
复制代码

Promise.prototype.finally

finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。

我们无论promise的状态如何,都在promise的最后面添加then,并传入了onFulfilled, onRejected两个回调。在回调中,我们执行finally的callback参数。这样无论之前的promise是fulfilled态,还是rejected态,都会执行finally添加的参数。


class Promise {// ...finally (callback) {// this指向调用finally的对象const self = this// 向Promise链中添加then,无论,promise是resolve态还是reject态,都会执行callback// 并且会通过then,继续将result或reason向下传递return self.then(result => Promise.resolve(callback()).then(_ => result),reason => Promise.resolve(callback()).then(_ => { throw reason }))}
}
复制代码

resolve

resolve用来修改promise状态,将promise状态设置为fulfilled态, 并执行then的onFulfilled回调

根据规范**2.3.1: If promise and x refer to the same object, reject promise with a TypeError as the reason.**如果promise与x相等,我们使用TypeError的错误拒绝promise

根据规范2.3.2。如果result是promise,并且处于pending态,promise需要保持pending态,直到result被执行和拒绝后,我们使用result的状态履行或者拒绝promise。如果result是promise,并且处于fulfilled或rejected态,我们使用result的状态拒绝或者履行promise。

根据规范2.3.3, 我们判断result是否为一个Object。如果result为Object, 我们则取出它的then的属性, 判断then属性是否为Function, 如果then为Function, 我们设置then的作用域的this指向result, 我们传入resolvePromise, rejectPromise作为参数。

根据规范2.3.4: If x is not an object or function, fulfill promise with x, 如果x不是函数或者对象,我们用result结果作为参数执行promise。


function resolve (promise, result) {if (promise === result) {throw reject(promise, new TypeError('promise and x refer to the same object'))}if (isPromise(result)) {if (result._state === pending) {result._tasks.concat(promise._tasks)} else if (result._state === fulfilled || result._state === rejected) {let taskwhile (task = promise._tasks.shift()) {handlePromise(result, task)}}return}if (isObject(result)) {let then = nulltry {then = result.then} catch (error) {reject(promise, error)}if (isFunction(then)) {try {let resolvePromise = function (y) {resolve(promise, y)}let rejectPromise = function (r) {reject(promise, r)}then.call(result, resolvePromise, rejectPromise)} catch (error) {reject(promise, error)}return}}promise._state = fulfilledpromise._value = resultif (promise._tasks && promise._tasks.length) {let task = nullwhile (task = promise._tasks.shift()) {handlePromise(promise, task)}}
}
复制代码

reject

reject将promise的状态设置为rejected, 并以当前的promise的状态,执行promise中通过then注册的onRejected回调。


function reject (promise, reason) {if (promise._state !== pending) {return}promise._state = rejectedpromise._value = reasonlet taskwhile (task = promise._tasks.shift()) {handlePromise(promise, task)}
}
复制代码

handlePromise

handlePromise函数主要根据当前的promise的状态, 以及内容(resolve或者reject的参数)。处理通过then注册的回调。并且会链式的调用,注册在then返回的新promise的上的then的回调


// 将回调的结果,传入第二个then中
fn().then().then()
复制代码

根据规范2.2.4, 以及规范给出的注解。当promise的状态改变,onFulfilled, onRejected并不会立即执行,而且在本次的宏任务完成后,才会执行onFulfilled或者onRejected。而setImmediate则是将代码push到微任务队列中。在宏任务中会清空微任务队列。


function handlePromise (prevPromise, task) {// 需要在宏任务完后的微任务队列中执行setImmediate(() => {// nextPromise是then返回的promiseconst { onFulfilled, onRejected, promise: nextPromise } = tasklet callback = nulllet value = prevPromise._valuelet state = prevPromise._stateif (state === fulfilled) {callback = onFulfilled} else if (state === rejected) {callback = onRejected}if (!callback) {// 如果在promise中没有注册callbackif (state === fulfilled) {resolve(nextPromise, value)} else if (state === rejected) {reject(nextPromise, value)}} else {try {const result = callback(value)// 对then中返回promise处理// 将callback返回的结果,带入到新的promise中resolve(nextPromise, result)} catch (error) {reject(nextPromise, error)}}})
}
复制代码

Promise.resolve & Promise.reject

Promise.resolve方法返回一个新的Promise对象,状态为resolved。Promise.reject(reason)方法也会返回一个新的 Promise实例,该实例的状态为rejected。

class Promise {// ...static resolve (result) {return new Promise((resolve) => { resolve(result) })}static reject (reason) {return new Promise((_, reject) => { reject(reason) })}
}
复制代码

Promise.all && Promise.race

Promise.all和Promise.race必须接受一个数组为参数,数组中为多个Promise的实例。Promise.all和Promise.race的使用我就不再这里赘述了。

Promise.all会使用计数器,记录Promise数组中的所有Promise实例的状态是否都变为fulfilled态,如果计数器的长度和数组长度一致,我们则会将Promise.all的状态设置为fulfilled态。


class Promise {static race (promises) {if (isArray(promises)) {let promisesLength = promises.lengthreturn new Promise((resolve, reject) => {for (let i = 0; i < promisesLength; i++) {promises[i].then((result) => {resolve(result)}).catch((error) => {reject(error)})}})} else {throw new TypeError('The arguments must be arrays')}}static all (promises) {if (isArray(promises)) {let promisesLength = promises.lengthlet counter = 0let resultList = []return new Promise((resolve, reject) => {for (let i = 0; i < promisesLength; i++) {promises[i].then((result) => {counter += 1resultList.push(result)if (counter === promisesLength) {resolve(resultList)}}).catch((error) => {reject(error)})}})} else {throw new TypeError('The arguments must be arrays')}}
}
复制代码

其他

VueRouter源码分析

Preact源码分析

转载于:https://juejin.im/post/5cb857a06fb9a068b16e1a60

自己鼓捣一个Promise相关推荐

  1. es6 --- 手写一个promise

    一个promise实例: var getJSON = function(url) {var promise = new Promise(function(resolve, reject) {// XH ...

  2. 面试官:能不能手写一个 Promise?

    大家好,我是若川.最近组织了源码共读活动,感兴趣的可以点此加我微信ruochuan12 进群参与,每周大家一起学习200行左右的源码,共同进步.已进行4个月了,很多小伙伴表示收获颇丰. 以下问题你是不 ...

  3. 面试官问:来实现一个Promise

    大家好,我是若川.最近组织了源码共读活动<1个月,200+人,一起读了4周源码>,已经有超50+人提交了笔记,群里已经有超1500人,感兴趣的可以点此链接扫码加我微信 ruochuan12 ...

  4. all方法 手写promise_试题-手写实现一个 promise.all - 拿OFFER

    Promise.all的实现和原理 Promise.all 功能: Promise.all(iterable)返回一个新的Promise实例,此实例在iterable参数内素有的Promise都ful ...

  5. Promise进阶——如何实现一个Promise库

    概述 从上次更新Promise/A+规范后,已经很久没有更新博客了.之前由于业务需要,完成了一个TypeScript语言的Promise库.这次我们来和大家一步一步介绍下,我们如何实现一个符合Prom ...

  6. 手写一个promise用法_手写一个自己的 JavaScript Promise 类库

    终于有时间写这篇文章了, ES2015 推出了JS 的 Promise ,而在没有原生支持的时候,我们也可以使用诸如 Promises/A+ 的库的帮助,在我们的代码里实现Promise 的支持: 如 ...

  7. promise原理—一步一步实现一个promise

    promise特点 一个promise的当前状态只能是pending.fulfilled和rejected三种之一.状态改变只能是pending到fulfilled或者pending到rejected ...

  8. Promise学习-手写一个promise

    学习了Promise的A+规范,以及手写一个Promise后,我对Promise学到的结果. 平常也有用到过promise来处理异步,先回顾下promise的用法 new Promise((resol ...

  9. JavaScript 怎么自己手写一个Promise

    认真看完这篇文章, 您可以自己封装一个简易但功能相对齐全的Promise, 还可以加深对Promise的理解 建议 : 看这篇文章之前希望您 了解ES6的语法 [ 阮一峰老师的ES6入门 ] 了解Pr ...

  10. 【JS】1126- 如何更好的取消一个promise?

    一个正在执行中的promise怎样被取消? 其实就像一个执行中的ajax要被取消一样,ajax有abort()进行取消,而且fetch api 也有了相关的规范-[AbortController]. ...

最新文章

  1. Linux基础—screen命令
  2. DAY11-MYSQL之ORM框架SQLAlchemy
  3. 典型案例 1:函数计算在音视频场景实践
  4. C#中使用Directory实现对文件夹的常用操作
  5. javap分析字符串拼接执行流程
  6. 互斥同步(synchronized、Lock、ReentrantLock、ReadWriteLock、ReentrantReadWriteLock)
  7. 除了TensorFlow、PyTorch,还有哪些深度学习框架值得期待?
  8. MySQL高级 - 锁 - InnoDB行锁 - 介绍及背景知识
  9. Spring源码情操陶冶-AbstractApplicationContext#registerBeanPostProcessors
  10. JS之iscroll.js的使用详解
  11. Innosetup打包自动下载.net framework 动态库及替换卸载程序图标.
  12. 面向对象类设计的五大原则(一)单一职责原则Single Responsibility Principle
  13. C++ 读取txt文件方法读取速度比较
  14. 视差图Disparity与深度图Depth Map的一点知识
  15. 将R Markdown文档变成互动体验
  16. struts1 和struts 2区别
  17. 前端js如何实现中文转拼音(重要)
  18. 如何对全站进行死链接检测?
  19. echarts图表x轴文字过多时换行显示以及图表画区域背景颜色
  20. python知识:装饰器@property有啥用途?

热门文章

  1. 前端js如何展示服务端保存到本地的照片(接收客户端上传的照片)
  2. 量子学习及思考7-量子基本数学知识
  3. 主持人台词没背熟,CCTV这次玩大了。。
  4. springboot整合消息索引
  5. 圣诞节礼物之——圣诞节主题的免费屏幕保护程序
  6. 蓝桥杯魔方旋转问题c++ ac
  7. SpringCloud的基本组件 五大神兽 SpringCloud和dubbo的区别
  8. Java 实现 Date日期+1天
  9. 从整数数组中找出唯二奇数次的数字 Python版
  10. 阿里云EMR2.0平台:让大数据更简单