javaScript 进阶之路 --- 《手写 Promise(中篇)》(二)

简介: javaScript 进阶之路 --- 《手写 Promise(中篇)》

三. 构思异步存储数据的思路


我们现在要明确一点,我们上面的代码 resolve 到底被调用了吗?会不会压根就是 resolve 没被调用才导致现在 then 拿不到数据呢?

我们在 resolve 里加一句打印,我们看看到底是不是这个原因。

image.png

在控制台可以清楚的看到,虽然没有第一时间执行,但是我们的 resolve 是确确实实执行了的。

70f5d4e4bd3e490186c0c3283ebbb0ea_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

清楚了这一点,我们需要理清楚思路。既然你 resolve 是在一秒后才会执行。如果我是 then 函数我可能会这样想:“resolve 函数啊,如果你执行完毕了以后你再通知我该有多好啊,别让我一个人先走一步~”

顺着这个思路,我们就要在 resolve 这里构思有什么方法可以去通知 then

image.png

这里我们再想想,我们的数据是在哪读取的?

image.png

没错,是在 then 函数的第一个回调函数 onFulfilled 里去读取的。那么有没有一种可能,我让你 resolve 去帮我执行这个 onFulfilled 函数不就更省心了吗?

image.png

这样我 then 函数坐享其成不是更美吗?

那么问题来了, then 函数的回调函数其实只能在 then 的作用域去调用,什么意思呢?我们给函数定义参数的时候,实际上是执行了下面的形参实参重新赋值的操作。所以我们的参数对外是压根看不见的。

image.png

换而言之,resolve 函数压根就不知道有 onFulfilled 这个函数!!!。

这就麻烦了,这怎么办呢?别急我们再定义一个变量,叫做 callBackFn,这个变量也是一个函数。

image.png

它用来干嘛呢?我们稍后揭秘,我想现在你的代码应该是下面这样子。

image.png

四. 神奇的回调函数


我们由上面可知,我们主要是因为 then 方法在 state==='pending' 的时候,没办法做任何操作才无法拿到异步函数传递过来的数据的。

image.png

注意接下来是全文重点: 当我在 state==='pending' 的时候,我把刚刚定义好的 callBackFn 函数值设置为 thenonFulfilled 回调函数的值。

image.png

接下来就是最神奇的一步操作,我再把 callBackFn 放到 resolve 函数拿到数据之后执行!

别着急,我们先试一下行不行再一步一步解释原因。还是之前的代码,按照下面代码的逻辑,我们应该会在两秒以后看到控制台输出 看看行不行 这个字符串。

image.png

我们看一下效果:

b5f157eb246f46a69603351f98c23faf_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

什么情况,还真可以!!

不要觉得这是什么黑魔法,其实思路特别特别简单。顺着之前的思路,当我们的代码执行以后。首先会执行。

image.png

我们的 resolve 就被丢进了 宏任务队列里去了。

4.然后主线程继续往下走,就走到了 then 函数中。

image.png

接下来我们就有需要跳到 MyPromise 类中去看 then 函数调用后发生的情况。首先我们非常明确的是,这时候由于 resolve 还没执行,所以我们的 state 还是 pending 状态。

image.png

那么这时候就会走 then 函数的第一个判断逻辑。

image.png

它会将我们 then 函数的第一个参数 onFulfilled 赋值给我们之前定义好的 callBackFn 变量。

至此,任何其他事情还没发生,然后我们静等了两秒以后,resolve 函数从任务队列里被推进了主线程。我们需要转头去看 resolve 函数执行。

image.png

我们可以非常非常清晰的看到,谁在最后执行了?没错!!就是我们两秒之前 被赋值为 onFulfilled 函数的 callBackFn 函数!!!

千万不要迷,这里完完全全就等价于两秒后 resolve 函数帮我去调了 then 函数的 onfulfilled 函数。

image.png

只不过我们没有办法直接去调用定义在 then 函数作用域的那个 onFulfilled ,而是通过了一个中间变量的形式,“间接去调用了它”。这便是 JS 回调函数的魅力所在。

五. 修复 bug


但是现在我们又产生了一个新的问题,当我们的 resolve 又变成了同步赋值的时候,我们看看是什么后果。

image.png

看一下控制台输出什么?

image.png什么情况?我们的 callBacnFn 又不是一个 function了?

你需要清晰的知道,如果我们的 resolve 没有放进 setTimeout 里的执行的话,它就是一个同步代码,同步代码的话,它就会在 then 函数执行之前执行。

image.png

反应在 MyPromise 类里面的执行过程就是。我们的 callBacnFn 在被赋值之前就被调用了,那肯定会报错啊,因为我们既没有给它赋初始值,又没有被 then 函数调用,所以它现在就是 undefined

image.png

那怎么办呢?其实非常非常简单,我们只需要在执行 callBackFn 之前,判断一下它存在不存在就可以了。

image.png

由于是教学,我们写一种更加容易理解的代码,如果有我就执行,如果没有我就不执行,就是这么简单。

image.png

我们看一下控制台输出还有问题吗:

image.png

ok,没问题了~

结语:


至此距离我们完成自己的 MyPromise 类已经成功了一大半!我相信通过消化这一篇的内容,你会收获很多很多额外的知识。是不是有一种原来 Promise 不过如此的感觉~

其实有很多很多东西都是用很基本的函数,通过很巧妙的设计去完成一些看起来很复杂的逻辑。在下一章我们会迎来最后的几个关键点,如:微任务的创建then函数的链式调用,希望你能坚持下去。🎁

如果你暂时还没读懂,没关系,我建议你先去看一下我们上面的几个支线任务 再回过头细细品味本篇内容。距离我们手撕 Promise 已经近在咫尺了,你的登神长阶完成了几章呢?加油,一起进步呀!冲鸭~

相关文章
|
12天前
|
XML 前端开发 JavaScript
JavaScript进阶 - AJAX请求与Fetch API
【7月更文挑战第3天】前端开发中的异步基石:AJAX与Fetch。AJAX,使用XMLHttpRequest,处理跨域、回调地狱和错误处理。Fetch,基于Promise,简化请求,但需注意默认无跨域头和HTTP错误处理。两者各有优劣,理解其问题与解决策略,能提升前端应用的性能和用户体验。
|
4天前
|
前端开发 JavaScript 安全
JavaScript进阶-JavaScript库与框架简介
【7月更文挑战第11天】JavaScript库和框架加速Web开发,但也带来挑战。选择适合项目、团队技能的库或框架,如React、Angular、Vue,是关键。保持依赖更新,注意性能优化,避免过度依赖。遵循最佳实践,确保安全性,如防XSS和CSRF。学习基础,结合代码示例(如React计数器组件),提升开发效率和应用质量。
|
6天前
|
前端开发 JavaScript
JavaScript异步编程:Promise与async/await的深入探索
【7月更文挑战第9天】Promise和async/await是JavaScript中处理异步编程的两大利器。Promise为异步操作提供了统一的接口和链式调用的能力,而async/await则在此基础上进一步简化了异步代码的书写和阅读。掌握它们,将使我们能够更加高效地编写出清晰、健壮的异步JavaScript代码。
|
10天前
|
资源调度 JavaScript 前端开发
JavaScript进阶 - JavaScript库与框架简介
【7月更文挑战第5天】JavaScript库和框架构成了前端开发的核心,如jQuery简化DOM操作,Angular、React和Vue提供全面解决方案。选择时要明确需求,避免过度工程化和陡峭学习曲线。使用版本管理工具确保兼容性,持续学习以适应技术变化。示例展示了jQuery和React的简单应用。正确选择和使用这些工具,能提升开发效率并创造优秀Web应用。
|
11天前
|
缓存 JavaScript 前端开发
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第4天】JavaScript的Web Workers和Service Worker增强了Web性能。Web Workers处理后台多线程,减轻主线程负担,但通信有开销,受同源策略限制。Service Worker则用于离线缓存和推送通知,需管理其生命周期、更新策略,并确保安全。两者都带来了挑战,但也极大提升了用户体验。通过理解和优化,开发者能构建更高效、安全的Web应用。
|
5天前
|
缓存 前端开发 JavaScript
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第10天】在Web开发中,Web Workers和Service Worker提升性能。Workers运行后台任务,防止界面冻结。Web Workers处理计算密集型任务,Service Worker则缓存资源实现离线支持。常见问题包括通信故障、资源限制、注册错误及缓存更新。通过示例代码展示了两者用法,并强调生命周期管理和错误处理的重要性。善用这些技术,可构建高性能的Web应用。
|
6天前
|
XML 前端开发 JavaScript
JavaScript进阶 - AJAX请求与Fetch API
【7月更文挑战第9天】JavaScript进阶:AJAX与Fetch API对比。AJAX用于异步数据交换,XMLHttpRequest API复杂,依赖回调。Fetch API是现代、基于Promise的解决方案,简化请求处理。示例:`fetch('url').then(r => r.json()).then(data => console.log(data)).catch(err => console.error(err))`。注意点包括检查HTTP状态、错误处理、CORS、Cookie和超时。Fetch提高了异步代码的可读性,但需留意潜在问题。
|
7天前
|
存储 JavaScript 前端开发
JavaScript进阶 - 浏览器存储:localStorage, sessionStorage, cookies
【7月更文挑战第8天】Web开发中的客户端存储技术,如`localStorage`, `sessionStorage`和`cookies`,用于保存用户设置和跟踪活动。`localStorage`持久化存储,`sessionStorage`随页面会话消失。两者提供基本的增删查改操作,但有大小限制和安全风险。`cookies`适合会话管理,可设置过期时间并能跨域。使用时注意存储量、安全性和跨域策略,选择适合场景的存储方式。
|
7天前
|
设计模式 JavaScript 前端开发
JavaScript进阶 - JavaScript设计模式
【7月更文挑战第7天】在软件工程中,设计模式是解决常见问题的标准解决方案。JavaScript中的工厂模式用于对象创建,但过度使用可能导致抽象过度和缺乏灵活性。单例模式确保唯一实例,但应注意避免全局状态和过度使用。观察者模式实现了一对多依赖,需警惕性能影响和循环依赖。通过理解模式的优缺点,能提升代码质量。例如,工厂模式通过`createShape`函数动态创建对象;单例模式用闭包保证唯一实例;观察者模式让主题对象通知多个观察者。设计模式的恰当运用能增强代码可维护性。
|
9天前
|
缓存 JavaScript 前端开发
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第6天】JavaScript的Web Workers和Service Worker增强了浏览器的性能处理和离线功能。Web Workers处理后台计算,减轻主线程压力,但通信有开销,受同源策略限制。Service Worker则能拦截网络请求,支持离线缓存和推送通知,但其生命周期和权限管理需谨慎处理。通过理解它们的工作原理和限制,开发者能创建更流畅、更健壮的Web应用。