js是什么意思(视觉)
admin
2023-08-26 11:43:33

作者:LydiaHallie

译者:徐颖全栈练习

原文链接:https://dev . to/lydiahallie/JavaScript-visualized-promises-async-awake-5g ke

正向链接:https://mp.weixin.qq.com/s/AthCvtiTnPETkAnzyOLN1A

原由

你有没有运行过没有按预期运行的js代码?

例如,一个函数在随机和不可预测的时间被执行,或者它的执行被延迟。

这时候你需要一个从ES6引入的非常酷的新功能:Promise来处理你的问题。

为了深入理解无极,我在一个不眠之夜做了一些动画演示无极的操作,多年的好奇终于实现了。

为什么要用Promise,它在底层是怎么运作的,我们怎么用最现代的方式来写?

介绍

在编写JavaScript时,我们经常要处理一些依赖于其他任务的任务!

例如,我们想要获取一张图片,压缩它,应用滤镜,然后保存它。

首先,使用getImage函数获取我们想要编辑的图片。

一旦图像加载成功,就将图像值传递给ocmpressImage函数。

成功调整图片大小时,在applyFilter函数中对图片应用滤镜。

图片压缩后,添加滤镜,保存图片,打印成功日志!

最后,代码非常简单,如图所示:

你注意到了吗?虽然上面的代码也能得到我们想要的结果,但是完成的过程并不友好。

使用了大量的嵌套回调函数,使得我们的代码特别难读。

因为写了很多嵌套的回调函数,所以这些回调函数依赖于之前的回调函数,也就是常说的回调地狱。

幸运的是,ES6中的承诺可能会很好地处理这种情况!

让我们来看看什么是承诺,以及在类似上述的情况下,它如何帮助我们。

Promise语法

ES6介绍了无极。在许多教程中,您可能会读到类似这样的内容:

承诺是一个价值的占位符,它将在未来的某个时刻被解决或拒绝。

对我来说,这样的解释从来没有让事情变得更清楚。

事实上,它只是让我觉得承诺是一个奇怪的,模糊的,不可预测的魔法。

接下来,让我们看看什么是真正的承诺。

我们可以使用接收回调函数的承诺构造函数来创建承诺。

酷,我们试试吧!

等等,你拿回了什么?

承诺是一个包含状态承诺和价值承诺的对象。

在上面的例子中,可以看到PromiseStatus的值是pending,PromiseValue的值是undefined。

但是——你永远不会和这个对象进行交互,甚至不能访问PromiseStatus和PromiseValue这两个属性!

但是,在使用Promise时,这两个属性的值非常重要。

PromiseStatus的值,即承诺的状态,可以是以下三个值之一:

履行:承诺已解决。一切都很好,承诺里面没有任何错误。rejected :承诺已被拒绝。哦,出问题了。待定:承诺尚未解决或拒绝,仍处于待定状态。所有这些听起来都很棒,但是承诺的状态什么时候是悬而未决,履行或拒绝?为什么这种状态很重要?

在上面的例子中,我们只是向Promise构造函数传递了一个简单的回调函数()={}。

然而,这个回调函数实际上接受两个参数。

第一个参数的值通常被称为resolve或res,这是一个当Promise应该解决resolve时会调用的函数。第二个参数的值通常被称为reject或rej,它也是当承诺中存在一些应该拒绝的错误时调用的函数。让我们看看调用resolve或reject方法时得到的日志。

在我的例子中,我调用resolve方法res和rej方法rej。

太好了!我们终于知道如何摆脱挂起状态和未定义值了!

当我们调用resolve方法时,承诺的状态就实现了。当我们调用reject方法时,承诺的状态被拒绝。有趣的是,我请(JakeArchibald)校对这篇文章,他竟然指出Chrome中有一个错误,目前显示的状态是& quot履行& quot

不是“resolved”。感谢MathiasBynens,它现已在Canary中修复!

好了,现在我们知道如何更好控制那个模糊的Promise对象。但是他被用来做什么呢?

在前面的介绍章节,我展示了一个获得图片、压缩图片、为图片应用过滤器并保存它的例子!最终,这变成了一个混乱的嵌套回调。

幸运的,Promise可以帮助我们解决这个问题!

首先,让我们重写整个代码块,以便每个函数返回一个Promise来代替之前的函数。

如果图片被加载完成并且一切正常,让我们用加载完的图片解决(resolve)promise。

否则,如果在加载文件时某个地方有一个错误,我们将会用发生的错误拒绝(reject)promise。

让我们看下当我们在终端运行这段代码时会发生什么?

非常酷!就像我们所期望的一样,promise得到了解析数据后的值。

但是现在呢?我们不关心整个promise对象,我们只关心数据的值!幸运的,有内置的方法来得到promise的值。

对于一个promise,我们可以使用它上面的3个方法:

.then():在一个promise被resolved后调用.catch():在一个promise被rejected后被调用.finally():不论promise是被resolved还是reject总是调用

.then方法接收传递给resolve方法的值。

.catch方法接收传递给rejected方法的值。

最终,我们拥有了promise被解决后(resolved)的值,并不需要整个promise对象!

现在我们可以用这个值做任何我们想做的事。


顺便提醒一下,当你知道一个promise总是resolve或者总是reject的时候,你可以写Promise.resolve或Promise.reject,传入你想要reject或resolve的promise的值。

在下面的例子中你将会经常看到这个语法。

在getImage的例子中,为了运行它们,我们最终不得不嵌套多个回调。幸运的,.then处理器可以帮助我们完成这件事!

.then它自己的执行结果是一个promise。这意味着我们可以链接任意数量的.then:前一个then回调的结果将会作为参数传递给下一个then回调!

在getImage示例中,为了传递被处理的图片到下一个函数,我们可以链接多个then回调。

相比于之前最终得到许多嵌套回调,现在我们得到了整洁的then链。

完美!这个语法看起来已经比之前的嵌套回调好多了。

宏任务和为任务(macrotaskandmicrotask)

我们知道了一些如何创建promise以及如何提取出promise的值的方法。

让我们为脚本添加一些更多的代码并且再次运行它:

等下,发生了什么?!

首先,Start!被输出。

好的,我们已经看到了那一个即将到来的消息:console.log('Start!')在最前一行输出!

然而,第二个被打印的只是End!,并不是promise被解决的值!只有在End!被打印之后,promise的值才会被打印。

这里发生了什么?

我们最终看到了promise真正的力量!尽管JavaScript是单线程的,我们可以使用Promise添加异步任务!

等等,我们之前没见过这种情况吗?

在JavaScriptEventLoop中,我们不是也可以使用浏览器原生的方法如setTimeout创建某类异步行为吗?

是的!然而,在事件循环内部,实际上有2种类型的队列:宏任务(macro)队列(或者只是叫做任务队列)和微任务队列

(宏)任务队列用于宏任务,微任务队列用于微任务

那么什么是宏任务,什么是微任务呢?

尽管它们比我在这里介绍的要多一些,但是最常用的已经被展示在下面的表格中!


(Macro)task:setTimeoutsetIntervalsetImmediateMicrotask:process.nextTickPromisecallbackqueueMicrotask

我们看到Promise在微任务列表中!当一个Promise解决(resolve)并且调用它的then()、catch()或finally()方法的时候,这些方法里的回调函数被添加到微任务队列!

这意味着then(),chatch()或finally()方法内的回调函数不是立即被执行,本质上是为我们的JavaScript代码添加了一些异步行为!

那么什么时候执行then(),catch(),或finally()内的回调呢?

事件循环给与任务不同的优先级:

当前在调用栈(callstack)内的所有函数会被执行。当它们返回值的时候,会被从栈内弹出。当调用栈是空的时,所有排队的微任务会一个接一个从微任务任务队列中弹出进入调用栈中,然后在调用栈中被执行!(微任务自己也能返回一个新的微任务,有效地创建无限的微任务循环)如果调用栈和微任务队列都是空的,事件循环会检查宏任务队列里是否还有任务。如果宏任务中还有任务,会从宏任务队列中弹出进入调用栈,被执行后会从调用栈中弹出!

让我们快速地看一个简单的例子:

Task1:立即被添加到调用栈中的函数,比如在我们的代码中立即调用它。Task2,Task3,Task4:微任务,比如promise中then方法里的回调,或者用queueMicrotask添加的一个任务。Task5,Task6:宏任务,比如setTimeout或者setImmediate里的回调

首先,Task1返回一个值并且从调用栈中弹出。然后,JavaScript引擎检查为任务队列中排队的任务。一旦微任务中所有的任务被放入调用栈并且最终被弹出,JavaScript引擎会检查宏任务队列中的任务,将它们弹入调用栈中并且在它们返回值的时候把它们弹出调用栈。

图中足够粉色的盒子是不同的任务,让我们用一些真实的代码来使用它!

在这段代码中,我们有宏任务setTimeout和微任务promise的then回调。

一旦JavaScript引擎到达setTimeout函数所在的那行就会涉及到事件循环。

让我们一步一步地运行这段代码,看看会得到什么样的日志!

快速提一下:在下面的例子中,我正在展示的像console.log,setTimeout和Promise.resolve等方法正在被添加到调用栈中。它们是内部的方法实际上没有出现在堆栈痕迹中,因此如果你正在使用调试器,不用担心,你不会在任何地方见到它们。它只是在没有添加一堆样本文件代码的情况下使这个概念解释起来更加简单。

在第一行,JavaScript引擎遇到了console.log()方法,它被添加到调用栈,之后它在控制台输出值Start!。console.log函数从调用栈内弹出,之后JavaScript引擎继续执行代码。

JavaScript引擎遇到了setTimeout方法,他被弹入调用栈中。setTimeout是浏览器的原生方法:它的回调函数(()=>console.log('Intimeout'))将会被添加到WebAPI,直到计时器完成计时。尽管我们为计时器提供的只是0,在它被添加到宏任务队列(setTimeout是一个宏任务)之后回调还是会被首先推入WebAPI。

JavaScript引擎遇到了Promise.resolve方法。Promise.resolve被添加到调用栈。在Promise解决(resolve)值之后,它的then中的回调函数被添加到微任务队列。

JavaScript引擎看到调用栈现在是空的。由于调用栈是空的,它将会去检查在微任务队列中是否有在排队的任务!是的,有任务在排队,promise的then中的回调函数正在等待轮到它!它被弹入调用栈,之后它输出了promise被解决后(resolved)的值:在这个例子中的字符串Promise!。

JavaScript引擎看到调用栈是空的,因此,如果任务在排队的话,它将会再次去检查微任务队列。此时,微任务队列完全是空的。

到了去检查宏任务队列的时候了:setTimeout回调仍然在那里等待!setTimeout被弹入调用栈。回调函数返回console.log方法,输出了字符串Intimeout!。setTimeout回调从调用栈中弹出。

终于,所有的事情完成了!看起来我们之前看到的输出最终并不是那么出乎意料。

Async/Await

ES7引入了一个新的在JavaScript中添加异步行为的方式并且使promise用起来更加简单!随着async和await关键字的引入,我们能够创建一个隐式的返回一个promise的async函数。但是,我们该怎么做呢?

之前,我们看到不管是通过输入newPromise(()=>{}),Promise.resolve或Promise.reject,我们都可以显式的使用Promise对象创建promise。

我们现在能够创建隐式地返回一个对象的异步函数,而不是显式地使用Promise对象!这意味着我们不再需要写任何Promise对象了。

尽管async函数隐式的返回promise是一个非常棒的事实,但是在使用await关键字的时候才能看到async函数的真正力量。当我们等待await后的值返回一个resolved的promise时,通过await关键字,我们可以暂停异步函数。如果我们想要得到这个resolved的promise的值,就像我们之前用then回调那样,我们可以为被await的promise的值赋值为变量!

这样,我们就可以暂停一个异步函数吗?很好,但这到底是什么意思?

当我们运行下面的代码块时让我们看下发生了什么:

额,这里发生了什么呢?

首先,JavaScript引擎遇到了console.log。它被弹入到调用栈中,这之后Beforefunction!被输出。

然后,我们调用了异步函数myFunc(),这之后myFunc函数体运行。函数主体内的最开始一行,我们调用了另一个console.log,这次传入的是字符串Infunction!。console.log被添加到调用栈中,输出值,然后从栈内弹出。

函数体继续执行,将我们带到第二行。最终,我们看到一个await关键字!

最先发生的事是被等待的值执行:在这个例子中是函数one。它被弹入调用栈,并且最终返回一个解决状态的promise。一旦Promise被解决并且one返回一个值,JavaScript遇到了await关键字。

当遇到await关键字的时候,异步函数被暂停。函数体的执行被暂停,async函数中剩余的代码会在微任务中运行而不是一个常规任务!

现在,因为遇到了await关键字,异步函数myFunc被暂停,JavaScript引擎跳出异步函数,并且在异步函数被调用的执行上下文中继续执行代码:在这个例子中是全局执行上下文!‍♀️

最终,没有更多的任务在全局执行上下文中运行!事件循环检查看看是否有任何的微任务在排队:是的,有!在解决了one的值以后,异步函数myFunc开始排队。myFunc被弹入调用栈中,在它之前中断的地方继续运行。

变量res最终获得了它的值,也就是one返回的promise被解决的值!我们用res的值(在这个例子中是字符串One!)调用console.log。One!被打印到控制台并且console.log从调用栈弹出。

最终,所有的事情都完成了!你注意到async函数相比于promise的then有什么不同吗?await关键字暂停了async函数,然而如果我们使用then的话,Promise的主体将会继续被执行!

嗯,这是相当多的信息!当使用Promise的时候,如果你仍然感觉有一点不知所措,完全不用担心。我个人认为,当使用异步JavaScript的时候,只是需要经验去注意模式之后便会感到自信。

当使用异步JavaScript的时候,我希望你可能遇到的“无法预料的”或“不可预测的”行为现在变得更有意义!

最后

外国友人技术博客的语言表达的方式和风格、与国人的还是有很大差别的啊。

明明看到有很长或者很拗口的句子的时候,我就想按自己的语言来写一篇了

可能自己写一篇都比翻译的快

推荐JavaScript经典实例学习资料文章

《原生JS封装拖动验证滑块你会吗?「实践」》

《如何实现高性能的在线PDF预览》

《细说使用字体库加密数据-仿58同城》

《Node.js要完了吗?》

《Pug3.0.0正式发布,不再支持Node.js6/8》

《纯JS手写轮播图(代码逻辑清晰,通俗易懂)》

《JavaScript20年中文版之创立标准》

《值得收藏的前端常用60余种工具方法「JS篇」》

《箭头函数和常规函数之间的5个区别》

《通过发布/订阅的设计模式搞懂Node.js核心模块Events》

《「前端篇」不再为正则烦恼》

《「速围」Node.jsV14.3.0发布支持顶级Await和REPL增强功能》

《深入细品浏览器原理「流程图」》

《JavaScript已进入第三个时代,未来将何去何从?》

《前端上传前预览文件image、text、json、video、audio「实践」》

《深入细品EventLoop和浏览器渲染、帧动画、空闲回调的关系》

《推荐13个有用的JavaScript数组技巧「值得收藏」》

《前端必备基础知识:window.location详解》

《不要再依赖CommonJS了》

《犀牛书作者:最该忘记的JavaScript特性》

《36个工作中常用的JavaScript函数片段「值得收藏」》

《Node+H5实现大文件分片上传、断点续传》

《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》

《【实践总结】关于小程序挣脱枷锁实现批量上传》

《手把手教你前端的各种文件上传攻略和大文件断点续传》

《字节跳动面试官:请你实现一个大文件上传和断点续传》

《谈谈前端关于文件上传下载那些事【实践】》

《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》

《最全的JavaScript模块化方案和工具》

《「前端进阶」JS中的内存管理》

《JavaScript正则深入以及10个非常有意思的正则实战》

《前端面试者经常忽视的一道JavaScript面试题》

《一行JS代码实现一个简单的模板字符串替换「实践」》

《JS代码是如何被压缩的「前端高级进阶」》

《前端开发规范:命名规范、html规范、css规范、js规范》

《【规范篇】前端团队代码规范最佳实践》

《100个原生JavaScript代码片段知识点详细汇总【实践】》

《关于前端174道JavaScript知识点汇总(一)》

《关于前端174道JavaScript知识点汇总(二)》

《关于前端174道JavaScript知识点汇总(三)》

《几个非常有意思的javascript知识点总结【实践】》

《都2020年了,你还不会JavaScript装饰器?》

《JavaScript实现图片合成下载》

《70个JavaScript知识点详细总结(上)【实践】》

《70个JavaScript知识点详细总结(下)【实践】》

《开源了一个JavaScript版敏感词过滤库》

《送你43道JavaScript面试题》

《3个很棒的小众JavaScript库,你值得拥有》

《手把手教你深入巩固JavaScript知识体系【思维导图】》

《推荐7个很棒的JavaScript产品步骤引导库》

《Echa哥教你彻底弄懂JavaScript执行机制》

《一个合格的中级前端工程师需要掌握的28个JavaScript技巧》

《深入解析高频项目中运用到的知识点汇总【JS篇】》

《JavaScript工具函数大全【新】》

《从JavaScript中看设计模式(总结)》

《身份证号码的正则表达式及验证详解(JavaScript,Regex)》

《浏览器中实现JavaScript计时器的4种创新方式》

《Three.js动效方案》

《手把手教你常用的59个JS类方法》

《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》

《深入浅出讲解js深拷贝vs浅拷贝》

《手把手教你JS开发H5游戏【消灭星星】》

《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》

《手把手教你全方位解读JS中this真正含义【实践】》

《书到用时方恨少,一大波JS开发工具函数来了》

《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》

《手把手教你JS异步编程六种方案【实践】》

《让你减少加班的15条高效JS技巧知识点汇总【实践】》

《手把手教你JS开发H5游戏【黄金矿工】》

《手把手教你JS实现监控浏览器上下左右滚动》

《JS经典实例知识点整理汇总【实践】》

《2.6万字JS干货分享,带你领略前端魅力【基础篇】》

《2.6万字JS干货分享,带你领略前端魅力【实践篇】》

《简单几步让你的JS写得更漂亮》

《恭喜你获得治疗JSthis的详细药方》

《谈谈前端关于文件上传下载那些事【实践】》

《面试中教你绕过关于JavaScript作用域的5个坑》

《Jquery插件(常用的插件库)》

《【JS】如何防止重复发送ajax请求》

《JavaScript+Canvas实现自定义画板》

《Continuation在JS中的应用「前端篇」》

作者:LydiaHallie

译者:xuying全栈修炼

原文链接:https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke

转发链接:https://mp.weixin.qq.com/s/AthCvtiTnPETkAnzyOLN1A

相关内容

热门资讯

金花创建房间/微信金花房卡怎么... 1.微信渠道:(荣耀联盟)大厅介绍:咨询房/卡添加微信:88355042 2.微信游戏中心:打开微...
金花房间卡/金花房卡如何购买/... 金花房间卡/金花房卡如何购买/新超圣金花房卡正版如何购买新超圣是一款非常受欢迎的游戏,咨询房/卡添加...
牛牛创建房间/金花房卡批发/神... 微信游戏中心:神牛大厅房卡在哪里买打开微信,添加客服【88355042】,进入游戏中心或相关小程序,...
链接牛牛/牛牛房卡游戏代理/鸿... 鸿运大厅房卡更多详情添加微:33549083、 2、在商城页面中选择房卡选项。 3、根...
科技实测!牛牛房卡怎么获得/乐... 微信游戏中心:乐酷大厅房卡在哪里买打开微信,添加客服【88355042】,进入游戏中心或相关小程序,...