1.从零开始手写Promise
2.Promise原理详解(二)
从零开始手写Promise
面试时,源码常被问及Promise应用;深入者或询问其实现细节,源码或查阅源码。源码本文聚焦于探究Promise内部如何实现链式调用。源码
所谓Promise,源码实质是源码稀缺单机手游源码一个容器,存储异步操作的源码结果。其提供统一接口,源码便于处理各种异步操作。源码
在Promise出现前,源码异步操作常通过回调函数实现,源码但过度嵌套引发回调地狱。源码Promise解决此痛点,源码简化回调复杂性。源码
Promise/A+规范,源码由社区提出,为业内所接受。规范定义Promise行为,包括状态转换不可逆,终值不可改变。
实现Promise需构造函数实例化对象,通过实例的多商户源码系统then方法处理异步结果。规范要求Promise必须包含等待态、执行态和拒绝态。
Promise构造函数立即执行,传入resolve和reject函数。异常情况通过try/catch捕获处理。
Promise状态一旦改变,无论成功或失败,都会触发then回调函数。回调函数需根据状态调用对应处理终值的函数。
规范允许onFulfilled和onRejected参数可选。实现时,对参数进行类型判断,忽略非函数参数。
通过一个四十行左右的简单Promise垫片,我们初步实现Promise基本结构与then方法。
链式调用是Promise核心。规范要求每个then方法返回新Promise对象,允许方法连续调用。
实现链式调用需返回新的Promise对象,避免调用时覆盖或丢失回调函数。通过返回Promise解决。人人矿场app源码
规范中,then方法返回Promise对象后,处理onFulfilled和onRejected时,需考虑值的传递特性。
Promise解决过程抽象,需输入Promise和值x。x为thenable对象时,接受x状态;否则使用x值执行。
实现解决过程时,首先排除传入参数自身情况,之后判断x是否为对象或函数,取then方法处理。
取then方法时,需使用try/catch捕获可能出现的错误,防止恶意代码导致程序崩溃。
对不同情况正确判断,处理函数调用,以及递归处理嵌套Promise,实现完整链式调用。
验证通过promises-aplus-tests工具,确保实现符合规范。vscode 查看go源码
了解更多前端知识,请关注公众号前端壹读。如认可内容,欢迎关注专栏或访问网站获取更多文章。
Promise原理详解(二)
在深入探讨Promise原理的第二部分中,我们继续从源码角度分析Promise的实现与使用。在上一节中,我们已了解了如何创建并赋值给Promise对象,以及在特定上下文下如何进行传递和调用。
Promise的核心方法之一便是`then`,它定义了访问Promise当前值、最终值及异常处理的机制。`then`方法可以接受两个参数,即成功回调函数`onFulfilled`和错误回调函数`onRejected`,这两个函数是可选的。当参数不是函数时,会发生值穿透。重要的是,`then`方法只能调用一次,但可以多次调用`then`以实现链式调用。
需要注意的条码app源码源是,`then`方法的返回结果必须是Promise对象。这意味着在调用`then`之后,返回的Promise对象将继承上一个`then`调用的返回值作为其参数传递给下一个`then`方法。
以示例代码为例,第二个`then`方法将利用上一个`then`调用返回的值。
接下来,我们将聚焦于`then`函数的内部实现,以深入理解其如何满足上述规范。
`then`函数首先获取当前Promise对象,并创建一个名为`child`的Promise对象,然后返回这个`child`对象,确保遵循`then`方法必须返回Promise对象的规则。
接下来的步骤涉及一系列判断和操作,这些操作主要围绕于确保Promise的生命周期和状态转换的正确性。首先,函数对`child`进行判断,若其未被初始化,便调用`makePromise`函数进行初始化。此步骤确保Promise对象的完整性。
接着,获取当前`then`方法所访问的Promise对象的`_state`属性。这个属性反映了Promise的状态:执行、拒绝或等待。基于此状态,`then`方法执行不同的逻辑操作。
当Promise处于执行或拒绝状态时,`then`方法会调用`invokeCallback`函数,执行相应的回调。而当Promise处于等待状态时,会调用`subscribe`函数,将回调函数添加至事件队列,等待Promise状态转换。
以代码为例,当存在一个`setTimeout`函数并延迟毫秒时,Promise的状态为等待状态。此时,`subscribe`函数被调用,将回调函数添加至事件队列,等待`setTimeout`触发。
`subscribe`接收四个参数:父级Promise对象、当前`then`方法返回的`child`对象、成功回调函数和拒绝回调函数。获取父级Promise的事件队列,并在队列尾部添加事件,确保回调函数的正确执行。
之后,检查事件队列的长度和父级Promise的状态,若非等待状态,则执行`publish`函数,将父级Promise作为参数传递给`publish`函数。至此,Promise的事件队列准备就绪,静待`resolve`或`reject`函数的触发。
在当前阶段,若`setTimeout`函数未返回值,事件队列已准备,静待Promise对象调用`resolve`或`reject`函数。这一阶段的流程梳理至此结束。
接下来,我们深入探讨`resolve`方法及其执行流程。假设`setTimeout`函数已触发。在深入分析之前,我们先回顾之前的方法调用流程,并在代码中找出关键点。接下来的分析将集中在`resolve`方法的具体实现及其对事件队列的处理过程。
当`resolve`函数被调用时,若其参数值是字符串类型,将直接进入`fulfill`函数。在`_subscribers`数组中,长度通常为3,因为已添加回调函数,`_subscribers`数组内容为`[child, callback(), null]`。
接下来,执行`publish`函数,获取事件队列。这里巧妙之处在于,通过`_state`属性来定位执行回调函数,`FULFILL`状态对应`1`,`REJECTED`状态对应`2`,以此精确确定执行的回调函数。
具体操作如下:第一个参数表示状态,第二个参数是Promise对象,第三个参数是回调函数,第四个参数是回调函数的参数。这里的`res`相当于第四个参数。
首先,判断回调函数是否为函数。如果不是,将值赋给`detail`,实现值穿透。如果回调函数是函数,则通过`try-catch`捕获异常,若捕获到异常,则将`error`赋值,否则设置`successed`为真值。确保`value`不等于Promise对象本身,避免递归死循环。此时,第一个`then`函数的执行完毕。
在上述分析的基础上,根据规范指导执行相应的操作。具体细节可参考相关文档。
特别提到的是,`resolve`方法的递归调用及其对事件队列的清空过程。在特定情况下,若`value`是对象或函数,处理方式与前述情况类似。然而,第二种情况的详细解释将留待下一节深入探讨。
在总结部分,我们简要介绍了`then`方法的其他分支,即当`_state`已经存在结果时,会立即执行`invokeCallback`函数,实现无回调情况下的即时结果返回。
最后,我们讨论了`Promise`的`asap`函数,其在特定环境下执行回调函数,确保Promise逻辑的正确执行顺序。通过`asap`函数的逻辑判断,我们可以理解其在不同环境下的实现机制,确保Promise能够在多种环境和条件下保持一致性和高效性。