起因
在老项目开发某功能,为了实现方便想用 promise,但因为浏览器兼容问题没有直接使用 promise,使用了 jquery 的 deferred ,对原生 promise 有了些疑问?
- jquery 怎么通过 deferred 支持类 promise 操作
- 怎么写个 promise-polyfill
先了解下 Promise
首先 Promise 是一种异步解决方案,更友好的解决 js 里的“地狱回调”。
比如:我们有多个异步操作,为了让变量按照预设逻辑执行,代码就会变成如下回调循环
1 | let data = 0; |
如果使用 Promise 就可以变成如下形式:
1 | new Promise((resolve, reject) => { |
对比 callback 方式,promise 无论在编码方式(异步转 同步),还是 可读性 上面都有这巨大的提升。
MyPromise 简单实现
1 | function MyPromise(lazyFn) { |
这里的实现牵扯一个重要的概念 —— 高阶函数
this._resolve 会返回一个高阶 function,并且这个 function 的参数 data 是 resolve(‘ok’) 中的 ok ;
在新建 MyPromise 对象后,将初始化一个延迟函数 this.lazyFn ,其需要一个 resolve 函数 ,就和原生 Promise 需要 resolve 和 reject 一样;
调用 then 后,传入 callback 函数,其是在延迟函数执行完,通过 this._resolve 返回的高阶函数后触发执行。
别人怎么做的
jQuery
简单的看下 jq 中 deferred 的 promise.then 的实现:
1 | tuples = [ |
tuples 定义了 promise 相关调用的 api ,调用 then 时就会遍历这些 api ,看哪些被用户定义使用;
比如上图的 ajax 调用,会把 jqXHR 推入到 deferred.promise 函数中,在 ajax 请求完毕后,执行对应的 resolve 方法。最后通知到 newDefer 执行对应 callback 。
promise-polyfill
相对于 bluebird ,promise-polyfill 是一个轻量级的“腻子”,点击直接查看源码
借它,看下正儿八经的 promise 如何实现:
- 首先初始化
在 new Promise 时调用创建
1 | function Promise(fn) { |
- 构建一个高阶函数,并执行
1 | function doResolve(fn, self) { |
fn 即是新建 Promise 传入的函数,并且参数列表 resolve、reject 是对应传入的两个函数。
1 | new Promise((resolve, reject) => { |
当 fn 执行完异步方法后,就触发:resolve(‘ok’)
- 执行 resolve function
1 | function(value) { |
注意:这里的 newValue 即是 resolve 实际的参数。会根据 newValue 类型来做后续的判断(promise 链式调用等)
这里 newValue 是 resolve(‘ok’) 中的 ok
1 | function resolve(self, newValue) { |
设置 Promise._state = 1,执行 finale
- 对 Promise 流程做最后的控制
1 | function finale(self) { |
当然由于 Promise._state == 1,Promise._deferreds.length == 0 ,所以什么都不会发生,只是运行了异步函数。
- then 的触发
一开始,用户会调用 then ,接受成功和失败两个方法,再次初始化 Promise ,作用就是构造一个空的 Promise chain;
并执行 handle ,对 Promise.deferreds 放入 new Handler ,注意这里的 this ,其实是一开始的 new Promise 的引用。
1 | Promise.prototype.then = function(onFulfilled, onRejected) { |
handle 会拿 deferred 的成功和失败做 callback 函数,并最后执行。
1 | function handle(self, deferred) { |
当然只是分析最简单的 Promise 方式,关于 Promise chain 就根据 self._deferreds 和 resolve 中 newValue 类型判断对 Promise._state 做不同的标记来区分
参考
我只是知识点的“加工者”, 更多内容请查阅原文链接 :thought_balloon: , 同时感谢原作者的付出: