You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called
functionnextTick(func){setTimeout(func)}classPromise{constructor(executor){this.value=undefinedthis.status='pending'this.children=[]executor(value=>{this.setStatus('resolved',value)},reason=>{this.setStatus('rejected',reason)})}then(onResolved,onRejected){varchild=newPromise(()=>{})this.children.push(child)child.parent=thisPromise.onChange()Object.assign(child,{onResolved: onResolved||(value=>value),onRejected: onRejected||(reason=>Promise.reject(reason))})if(this.status!=='pending'){child.triggerHandler(this.status,this.value)}returnchild}catch(onRejected){returnthis.then(null,onRejected)}triggerHandler(status,parentValue){// privatenextTick(()=>{varhandlerif(status==='resolved'){handler=this.onResolved}elseif(status==='rejected'){handler=this.onRejected}this.setStatus('resolved',handler(parentValue))})}setStatus(status,value){// privateif(value&&value.then){value.then(realValue=>{this.setStatus('resolved',realValue)},reason=>{this.setStatus('rejected',reason)})}else{this.status=statusthis.value=valuePromise.onChange()this.children.forEach(child=>{child.triggerHandler(status,value)})}}staticresolve(value){returnnewPromise(resolve=>{resolve(value)})}staticreject(reason){returnnewPromise((resolve,reject)=>{reject(reason)})}staticall(){/* TODO */}staticrace(){/* TODO */}}
Promise 的可视化
为了让女票理解 Promise, 做了一个可视化展现整个 Promise 状态变化的动态效果
可视化在线 demo
其中: 延迟时间, 状态是 resolved / rejected, 以及挂
.then
的数量都是随机的Javascript 异步的历史
了解 Promise 是感受前端近几年变化的最好方式
还记得2014年, 前端还是宇宙初始一片混沌
大家都用着最原始的写法, 甚至分不清为什么 setTimeout 取到的
i
会不正常后端大家弄懂了异步, 还约定了
callback(err, value)
第一个值是错误再后来, 前端被分为五岳剑派
2014年 Promise 从 whatwg 的 dom 规范中删除, 被分配到 es2015 中去了
现在, Promise 一统江湖~
Promise 并非是 javascript 初创, E语言, C++, python 都有比 javascript 早的 Promise 概念
为什么要有 Promise?
我们写的简单逻辑大多是简单写法
但现实生活是我们的很多操作是很复杂的, 可能是发一个请求, 可能是按一个按钮, 可能是读一下硬盘
这些操作并非瞬间完成, 比如发请求, 慢的时候要花好几秒
此时我们就发明了 callback
这种写法也叫 CPS, CPS 的全称是 (Continuation-Passing Style)
CPS 就是 放弃 return 语句, 返回值始终通过一个 callback 来传递
thunk
也是一个典型的 CPS 设计很显然, 把callback的写法变成同步的串行的是我们的终极目标, async/await 已经帮我们解决了此问题, 大幅提升代码可读性, js 的异步问题终于大一统了
四年后的我再次教女票实现 Promise, 更加被 Promise 的整个设计所惊艳, 可以说 Promise 真正意义上解决了现实问题
Promise 现场教学开始了!
then 约定
最初的 Promise 其实叫 thenable, 现代的 Promise 也是 thenable 的扩展
thenable 是这样约定的, 一个 thenable 对象, 可以这样获取它的值
当然, 仅仅这么简单肯定是不够的, 我们来看看现代的 Promise 长什么样
定义 Promise 类
class 是另一个我非常钦佩的设计, 一个 class, 就能说清楚自己的一片天
Promise 这个类, 无非是一个构造器, 两个方法, 两个属性, 几个静态函数
Promise 的状态
有人说 Promise 是状态机, 无可厚非, Promise 约定了三种状态
pending
默认值resolved
rejected
状态只有两种变化
pending
=>resolved
pending
=>rejected
构造函数
一个初始化的 promise 用 Promise 类来产生, 构造器的参数是一个叫 executor 的函数, 里面提供了两个方法来修改 promise 的状态
实现
.then
Promise 的核心在于
.then
, 它通过一个 handler 来接收值, 这也是 thenable 的约定不过这样有很多问题
万一 promise 还在 pending 的时候就挂了 then 呢?
解决办法:
我们想到
jQuery(function() {})
支持始终在 document 加载成功后执行, 不管我们写的时候 document 加载了没有, 因此 then 也用类似的思路也行then 的值应该可以不断传下去
我们再优化一下 then 函数
我们还想到, then 其实是可以无限 then 下去的, 这时候我们第一反应依然是 jQuery, jQuery 的链式调用非常好用, 秘诀是返回了
this
那 Promise 的 then 可不可以也直接返回
this
来实现链式调用呢?我们先试试假如 then 返回
this
最后的 then 获取到的值已经被 promise2 的返回值改变了, 这显然不是我们想要的
then 始终返回一个新的 Promise
为了简单理解可以把 then 理解为生孩子, 每次
.then
都会生出一个新的 promisePromise 就是 传宗接待, 开枝散叶!
王氏一家不会因为生了一个孩子名字叫 "小明", 而把妈妈的名字也改成 "小明"
但妈妈每生出一个孩子都可以随父辈王氏的姓
也就是说 Promise 的链式操作和 jQuery 的链式操作截然相反, jQuery 链的自己, Promise 链的是子子孙孙
Promise 可以不断
.then
同一个 promise, 我们突然惊醒, Promise 不仅仅是一个链, 它甚至可以是一片森林!如果理解了这个, 我们会发现之前的 then 实现都是错的, 我们只能推倒重来!
then 有两个 handler,
onResolved
和onRejected
这个 handler 概念很像 dom 中的 onclick, 我们可以同样把 resolved 和 rejected 看成是两个状态
因此我们定义一个
triggerHandler
专门用来触发自己的 handler再定义一个
setStatus
用来设置 Promise 自己的状态和值, 同时, 我们要在设置新状态之后通知自己的孩子们Promise 的值可以是另一个 Promise
我们知道, Promise 的 then 是可以不断执行异步函数的, 就好像一个任务清单那样
但按照目前的实现, then 之后只能链式同步任务了, 显然不能满足我们的需求
再次优化 Promise 的
setStatus
, 使其可以支持 Promise 类型的值, 这样我们可以把新的 Promise 任务塞进自己的孩子Promise 中的 nextTick
Promise/A+
规范要求 handler 执行必须是异步的, 具体可以参见标准 3.1 条我们这里用
setTimeout
简单实现一个跨平台的nextTick
使用
nextTick
包裹triggerHandler
如果你是好奇宝宝, 刨根问底的问为啥一定要 nextTick ?
那欢迎阅读我另一篇博客为什么 Promise 的 onFulfilled 和 onRejected 必须是异步的?
完整的实现
要注意, 一个规范总是非常详细的, 里面有各种各样的细节, 但这对我们理解 Promise 的设计并没有帮助, 反而会带来一些干扰
此处贴上边指导女票边写的 Promise 实现
更完整更严谨的 Promise 可以参照我之前写过一个通过官方800多个测试 的 Promise 项目 min-promise
The text was updated successfully, but these errors were encountered: