Promise原理分析
前言
最近讨论到了 Promise
,此前知道也使用过它,但是对于其原理却不甚了解。
于是翻了翻 MDN
上的文档,又找了几篇文章看了看,研究了研究。
最终,自己尝试了一番,对于其原理也有所了解。
Promise的使用
先回顾一下 Promise
的使用。
这里只是简单的调用,如果需要系统学习,还是移步 MDN
上的文档。
new Promise((resolve) => {setTimeout(() => {console.log(1)resolve(1)})
})
.then((response) => (console.log(++response), response))
.then((response) => {return new Promise((resolve) => {setTimeout(() => {console.log(1)resolve(1)})})
})
.then((response) => (console.log(++response), response))
执行结果如下:
代码分析
一个
Promise
就是一个代表了异步操作最终完成或者失败的对象。---- 摘自 MDN
第一步,实例化一个 Promise
对象。
它的参数就是一个函数,此函数会接受两个参数,分别是 resolve
和 reject
。
当操作成功时,则执行 resolve
。
当操作失败时,则执行 reject
。
new Promise((resolve, reject) => {setTimeout(() => {console.log(1)resolve(1)})
})
回调之后的操作可以用其提供的 then
方法。
then
接受两个参数,分别是 resolve
和 reject
。原理同上。
new Promise((resolve) => {setTimeout(() => {console.log(1)resolve(1)})
})
.then((response) => (console.log(++response), response))
从上述代码里可以看出,我们使用了 setTimeout
。
也就是说,then
方法会在 setTimeout
里的方法之前执行。
如果没有 Promise
的特殊机制,输出的结果可能就是 undefined、undefined、1、1
了。
这就是 Promise
的魅力,也是我想要知道的地方。
原理分析
既然是要分析其原理,那么按照其特性和使用方式来仿造一个是一个不错的选择。(主要是我不知道它的源码是什么<(_ _)>)
基本结构
先实现实例化的部分。
从上面的代码,我们已经知道,实例化的时候,只需要传 一个参数 就可以了,同时这个 参数 是一个 函数。
从执行结果来看,在我们实例化的时候,这个 函数 已经 执行 了。
并且这个函数会接收到 两个参数,这 两个参数 都是 函数。
function PromiseDemo(foo) {if (typeof foo != 'function') {throw Error('Promise 的参数必须是函数')}// 成功后执行的函数let resolve = response => {}// 失败后执行的函数let reject = response => {}// 实例化后,立即执行函数foo(resolve, reject)
}
then
方法部分。
then
方法是 Promise
对象的一个方法,我们需要将其暴露出来。
同时,then
方法支持链式的调用,所以返回值肯定是一个 Promise
对象。
function PromiseDemo(foo) {// 成功后执行的函数let resolve = response => {}// 失败后执行的函数let reject = response => {}// 实现 then 函数let then = (onResolved, onRejected) => {return new PromiseDemo((resolved, rejected) => {// todo...})}// 实例化后,立即执行函数foo(resolve, reject)return {then: then}
}
到这里,基本的结构已经有了,我们已经可以执行相关代码了,就是还没有想要的效果。
属性完善
通过一开始的示例分析,我们可以看出,then
参数接收到的参数是从上层传递到下层的。
这句话有点绕,参数的参数。所以,Promise
对象就要有一个存放返回值的属性,这里就给它命名为 data
。
同时,then
函数的参数,是在 setTimeout
执行之后才执行的。那么,Promise
对象就要有一个存放任务的列表,这里就给它命名为 queue
。
到这里,问题就来了,什么时候应该将任务放入任务列表呢?是还没有执行的,还是执行成功的,还是执行失败的?
说到这里,我们就要有一个标识表明这个任务是处于什么状态,是未执行,还是已执行,还是执行失败。这里就给它命名为 state
,值分别为 pending
、resolved
、rejected
。
上述文字有点长,主要就是定义了 Promise
的三个属性:state
、queue
、data
。
let state = 'pending', queue = [], data
实现 then
函数
为什么不返回 this
上面分析实例的时候有说到,为了能够达成链式调用,我们在 then
函数的返回值要是一个 Promise
对象。
而且基础代码里编写的是直接实例化了一个新的对象,那么可能会造成疑惑,直接返回 this
不行么?
答案是否定的,原因很简单,是由于 Promise
自身的属性决定了不能直接返回 this
,不然其中任何一个属性发生修改都会对后面的操作造成影响。
编码
回归正轨。
let then = (onResolved, onRejected) => {return new PromiseDemo((resolved, rejected) => {// todo...})
}
then
函数有两个参数,并且返回值是一个实例化的 Promise
对象。
也就是说,在处理的过程中,我们将会涉及四个函数,分别是:onResolved
, onRejected
, resolved
, rejected
。
先分析一下四个函数的执行情况。
如果之前的 Promise
尚未处理(使用的异步代码,比如:setTimeout
, ajax
),那么当前的状态 state = 'pending'
,那我们就不能执行任何一个函数。
这个时候我们就要将这四个函数存储好,表明这属于同一个任务。
// 如果还没执行结束,则将任务推向队列,并返回
if (state == 'pending') {queue.push([onResolved, onRejected, resolved, rejected])return
}
如果之前的 Promise
已经执行,那么当前的状态 state = 'resolved'
或者 state = 'rejected'
。
这个时候我们就要分辨到底是什么情况了。
// callback 是 then 函数两个参数中的一个
// next 是 Promise 成功失败函数中的一个
let callback, next
// 判断是否出错
if (state == 'resolved') {callback = onResolvednext = resolved
} else {callback = onRejectednext = rejected
}
当我们处理完之后,我们就要执行相关函数了。
callback(data)
为了能够将参数传递给下游,callback
的返回值需要直接给 next
使用。
next(callback(data))
好了,结束。
真的结束了么?
由于 callbak
是用户调用 then
函数传递过来的,那用户真的一定会传参数么?用户传过来的参数一定是一个函数么?那么直接执行 callback
就会有问题!!!
// 如果是函数,则直接执行
// 并将参数传递给下游
if (typeof callback == 'function') {next(callback(data))return
}
next(data)
好了,这样貌似没什么问题了,整理一下代码。
function then(onResolved, onRejected) {return new PromiseDemo((resolved, rejected) => {handle(onResolved, onRejected, resolved, rejected)})
}function handle(onResolved, onRejected, resolved, rejected) {// 如果还没执行结束,则将任务推向队列,并返回if (state == 'pending') {queue.push([onResolved, onRejected, resolved, rejected])return}let callback, next// 判断是否出错if (state == 'resolved') {callback = onResolvednext = resolved} else {callback = onRejectednext = rejected}// 如果是函数,则直接执行// 并将参数传递给下游if (typeof callback == 'function') {next(callback(data))return}next(data)
}
实现 resolve
函数
相对于 then
函数的复杂,resolve
函数就相对简单点。
为了避免重复执行,这里直接将 state
置为 rejected
。
先考虑一下,如果传过来的值是一个 Promise
对象,那我们该怎么处理?如果是一个非 Promise
对象的参数,该怎么处理?
由于 Promise
对象本身是拥有 then
函数的,我们应该执行它,这样这个对象处理后的值,会流转到下一层。
let resolve = response => {state = 'resolved'if (response && (typeof response == 'function' || typeof response == 'object')) {if (response.hasOwnProperty('then') && typeof response.then == 'function') {response.then.call(response, resolve, reject)return}}data = responserunCallbacks()
}
这里有个 runCallbacks
是干嘛的呢?
还记得我们将未执行的任务放到 queue
中了不?那么当前的任务执行完,是不是该执行下一个任务了呢?
let runCallbacks = () => {queue.forEach(callback => handle.apply(null, callback))
}
OK,到这里,resolve
函数已经编写完成了。
实现其它
Promise
还有其它的一些方法,例如:reject
、catch
等。
这里就不一一写了,下面该试验一下我们的 PromiseDemo
是否有用了。
demo 测试
先来源码
function PromiseDemo(foo) {if (typeof foo != 'function') {throw Error('Promise 的参数必须是函数')}let state = 'pending', queue = [], datafunction then(onResolved, onRejected) {return new PromiseDemo((resolved, rejected) => {handle(onResolved, onRejected, resolved, rejected)})}function handle(onResolved, onRejected, resolved, rejected) {// 如果还没执行结束,则将任务推向队列,并返回if (state == 'pending') {queue.push([onResolved, onRejected, resolved, rejected])return}let callback, next// 判断是否出错if (state == 'resolved') {callback = onResolvednext = resolved} else {callback = onRejectednext = rejected}// 如果是函数,则直接执行// 并将参数传递给下游if (typeof callback == 'function') {next(callback(data))return}next(data)}let runCallbacks = () => {queue.forEach(callback => handle.apply(null, callback))}let reject = response => {state = 'rejected'data = responserunCallbacks()}let resolve = response => {state = 'resolved'if (response && (typeof response == 'function' || typeof response == 'object')) {if (response.hasOwnProperty('then') && typeof response.then == 'function') {response.then.call(response, resolve, reject)return}}data = responserunCallbacks()}try {foo(resolve, reject)} catch (e) {reject(e)}return {then: then}
}
执行一开始的 demo
,结果如下:
流程图
说也说了,写也写了,整个流程是什么样子的呢?
参考资料
Promise原理解析
深入理解 Promise
最后
终于写完了,有点啰嗦,基本上把我想表达的都表达出来了。
– EOF –
本文转载自IMJCW
原文链接:Promise原理分析
Promise原理分析相关推荐
- Promise原理详解及实现方式
在异步编程中,许多操作都会放在回调函数(callback)中,有时候需要拿到上一个异步操作的返回值再做第二次请求 比如: asyncOperation(data => {// 处理 `data` ...
- Promise原理及手写Promise
原理: Promise 原理围绕以下三个问题进行解决: (有任何一步不了解的话请先往下看手写Promise,会一步步剖析原理,看完后再继续回顾这里!!) 1. 怎么实现异步? Promise内部the ...
- Promise原理及实现
Promise原理及实现 1 Promise核心逻辑实现 2 加入异步逻辑 3 then方法添加多次调用逻辑 4 链式调用then方法 5 Promise错误捕获 6 then方法参数设置为可选 7 ...
- java signature 性能_Java常见bean mapper的性能及原理分析
背景 在分层的代码架构中,层与层之间的对象避免不了要做很多转换.赋值等操作,这些操作重复且繁琐,于是乎催生出很多工具来优雅,高效地完成这个操作,有BeanUtils.BeanCopier.Dozer. ...
- Select函数实现原理分析
转载自 http://blog.chinaunix.net/uid-20643761-id-1594860.html select需要驱动程序的支持,驱动程序实现fops内的poll函数.select ...
- spring ioc原理分析
spring ioc原理分析 spring ioc 的概念 简单工厂方法 spirng ioc实现原理 spring ioc的概念 ioc: 控制反转 将对象的创建由spring管理.比如,我们以前用 ...
- 一次 SQL 查询优化原理分析(900W+ 数据,从 17s 到 300ms)
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:Muscleape jianshu.com/p/0768eb ...
- 原理分析_变色近视眼镜原理分析
随着眼镜的发展,眼镜的外型变得越来越好看,并且眼镜的颜色也变得多姿多彩,让佩戴眼镜的你变得越来越时尚.变色近视眼镜就是由此产生的新型眼镜.变色镜可以随着阳光的强弱变换不同的色彩. 变色眼镜的原理分析 ...
- jieba分词_从语言模型原理分析如何jieba更细粒度的分词
jieba分词是作中文分词常用的一种工具,之前也记录过源码及原理学习.但有的时候发现分词的结果并不是自己最想要的.比如分词"重庆邮电大学",使用精确模式+HMM分词结果是[&quo ...
最新文章
- vc开发soap客户端(方式一)
- [AGC016B]Colorful Hats
- hot编码 字符one_用 PyTorch 实现基于字符的循环神经网络 | Linux 中国
- 二叉树遍历(信息学奥赛一本通-T1364)
- eclipse.jsp文件放哪_来自小师弟的灵魂拷问之数据泵导出丢失的那些数据量去哪了?...
- 在XIB里面关于@property,@synthesize,release,dealloc的怪现象
- FineReport的公式编辑框的语法简介
- emacs VS vim 替换为回车符
- 【机器学习】决策树的理论与实践
- Hadoop安装部署的三种模式总结
- FPS游戏方框透视基本原理
- dnf全部使用_DNF所有职业通用的CD配装分享 技能无限制使用
- logit方程怎么写_家长也能看懂的“一元一次方程解法”,请大家收藏给孩子看!...
- 德琪医药和上药控股达成合作;方达医药位于美国宾州新实验室投运;药明康德发布财报 | 医药健闻...
- telephony相关修改点
- Java 以任意数量空格分割字符串方式
- php采集喜马拉雅,喜马拉雅数据 JSSDK API 接入 demo WEB版 标准登录 、免登陆
- 2020.11.3--AE--将PSD/AI文件导入AE、界面基本操作、形状图层
- 渗透分支写脚本_抖音文案怎么写吸引人?最新文案创作技巧分享(赠文案脚本模板)...
- Android九宫格界面实现点击每个格点击跳转界面