-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
剖析 Promise 内部机制 #14
Labels
Comments
最近你挖的坑比较多啊 |
@XueRainey 我也不想挖坑,工作事太多了。这些都是夹缝中抽出的时间。 |
@XueSeason 我都周末有时间会写点东西,平常代码都来不及写。。。 |
我周末不加班就陪老婆了 只有碎片时间看东西 没时间写啊 |
终于完成的差不多了。只有深入理解才知道,简单的几十行 Promise 代码,能说的还有很多。时间仓促,基本就这样把核心讲完吧。 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
从回调地狱说起 Callback Hell
在 Node 中,绝大部分操作都是异步的方式,例如读取一个文件内容:
这里我们通过传入一个回调函数作为异步完成的后续操作。
如果我们异步读取多个文件,等到所有文件读取完毕执行特定操作呢?
代码修改如下:
这里还是简单地敲套三层,想象下业务突然复杂,一不小心嵌套五层以上,这画面太美。。。
救世主 Promise
我们可以把上面 Node 读取文件的操作改造如下:
现在来看看 Promise 是如何解决 Callback Hell 的:
Promise 是一种对异步操作的封装,在异步操作执行成功或者失败时执行指定方法。将横向的异步调用转换为纵向,因此更符合人类的思维方式。
一个 Promise 对象具备三种生命状态:pending、fulfilled 和 rejected。只能从最初的 pending 到 fulfilled 或者 rejected,而且状态的改变是不可逆的。
我们先简单看下 Promise 的工作原理。
Promise 大致的工作流程是:
这里创建一个 Promise 对象,Promise 内部维系着 resolve 和 reject 方法,resolve 会让 Promise 对象进入 Fulfilled 状态,并将 resolve 方法的第一个参数传给后续 then 所指定的 onFulfilled 函数。reject 方法同理,只不过是切换到 Rejected 状态,并将参数传给 catch 所指定的 onRejected 函数。
一步步打造心中的 Promise
基础实现
先抛开 rejected,实现一个 Promise 的调用链的简单代码如下:
深入理解上面代码逻辑:
但是这段代码暴露出一个严重的问题,如果 Promise 执行的是同步代码,resolve 是早于 then 方法的执行,这样造成一个问题:then 还没有及时把 onFulfilled 函数压入队列,此时 deferreds 还是空数组,resolve 执行后,后续注册到 deferreds 数组内的 onFulfilled 函数将不再执行。
这里我们可以把 deferreds 数组视为水桶,onFulfilled 视为饮用水,resolve 视为开关。then 操作就是将饮用水一点点地注入到水桶中。想想我们还没将水加到水桶中(执行 then 操作)就打开开关(执行 resolve),这肯定是接不到水的。
解决的办法就是将 resolve 函数的执行改为异步。
异步
Promises/A+ 规范明确要求回调需要通过异步方式执行,保证一致可靠的执行顺序。通过 setTimeout 方法,我们可以轻松实现:
这样我们就可以把 resolve 执行放到下一个时钟周期。
引入状态
按照 Promise 规范,我们需要引入三种互斥的状态:pending、fulfilled、rejected。
执行 resolve 会将 pending 状态切换到 fulfilled,在此之后添加到 then 到函数都会立即被调用。
现在我们的代码如下:
有了上面的基础,我们可以简单地调用 Promise:
为了串行 Promise,我们在 then 中返回 this,并设置一个 value 来保存传给 resolve 的值。
像上面这样调用,虽然可以通过,但是两次输出的 data 是相同的值,并不是真正意义上的链式调用。
串行 Promise
只要 then 方法每次调用都返回一个 Promise 对象,前一个 Promise 对象持有后一个 Promise 对象的 resolve 方法,这样串行就变得非常简单了。
这里需要对 then 方法进行改造:
这里完成的主要任务是:
handle 相比之前的 then 多了一行
deferred.resolve(ret)
,这一步是链式调用的关键点。此刻的 resolve 是下一级 Promise 的方法,上一级 Promise 执行这段方法调用,就开启了链式调用。我们继续重构前面的 Promise 代码,这里主要修改的是 resolve 方法。
Promise 具体流程
pending
。每一个被创建出来的新 Promise 的 resolve 都将传给上一级的 Promise 的 deferreds 数组来维护
fulfilled
,然后异步调用 handle 依次处理 deferreds 数组中的每一个 deferred。fulfilled
,重复步骤 6 直到结束。这样 Promise 的核心逻辑,基本被我们实现了。至于 rejected 和 异常处理 就交给大家来思考吧(其实就是懒!)。
结束语:真的很难想象就这么几十行代码,竟然有如此强大的威力,理解 Promise 并不难,需要敬佩的是创造强大 Promise 魔法的第一批程序员。
The text was updated successfully, but these errors were encountered: