我们根据Ecmascript规范学习承诺。 熟人

承诺介绍

你好 在研究JavaScript(原则上是任何其他技术)后,总会出现各种各样的问题,其主要是:“为什么这样工作而不是其他方式?”这时非常重要,不仅要找到问题的答案,还要找到收到的解释。嵌入到已经获得的知识的单个系统中。 否则,必须记住或遗忘孤立的信息。


一起学习可以帮助您找到答案。 当一个学生/同伴问一个有关如何理解短语的问题时,“……前一个的结果”在链中的下一个承诺中“失败”……一个人不由自主地认为……这是一件很奇怪的事情。 但是您不能再说更好了,真的不清楚吗? 您看着干净,略带天真的同伴的眼睛并明白了-您需要说些其他话。 希望您甚至不必记住。 为了使新信息有机地适应现有的人类思想。


我不会描述我们尝试,阅读和观看的内容。 结果,我们对ECMAScript规范产生了兴趣。 如何阅读和理解它是一个单独的对话(甚至可能是一个单独的帖子)。 但是在那里第一次描述了承诺及其行为的方式,这首次使我们对该主题有了全面而逻辑上的理解。 我想与您分享什么。


本文适用于初学者。 关于ECMAScript规范的承诺将在这里讨论。 我知道这听起来很奇怪,但是确实如此。


承诺对象:其理念,技术介绍,可能的状态


我已经不止一次地注意到,高质量的编程培训应该包括两部分。 这是对该概念的哲学理解,然后才是其技术实施。 就是说,学生在做出任何决定时所遵循的通常的人类逻辑极大地促进了对该决定的技术实施的理解。 因此,我们从生活中的承诺开始,如何与之联系? 然后,我们将看到:如何在代码中实现promise的示例。 考虑下图(图1、2、3)。


承诺状态
图1。([[PromiseState]]-作为承诺的结果)

承诺结果
图2。([[PromiseResult]]-与已履行或未履行承诺的结果有关的信息)

无极的反应
图3.([[[[PromiseFulfillReactions]],[[PromiseRejectReactions]]-作为在履行或未能履行承诺之后发生的后果)

我们看到,诺言的概念站在三个支柱上。 1)诺言完全兑现了吗? 2)在履行或拒绝诺言后,我们还能提取哪些其他信息? 3)如果我们的承诺是正面还是负面的后果是什么?


从技术上讲,promise是通过诸如对象之类的数据类型表示的普通实体。 该实体的名称/类别为Promise。 从此类出生的对象的原型链中具有Promise.prototype。 这个实体必须以某种方式与我们上面研究的所有“生命信息”相关。 ECMAScript规范甚至在抽象度低于JavaScript本身的水平上,也将这些信息置于承诺之中。 例如,在C ++级别。 因此,在承诺的目标中,既有地位,又有结果,又有承诺的后果。 看一下ECMAScript的承诺(图4)。


承诺领域
图4.(根据ECMAScript规范的promise对象的内部字段)

就程序员而言,“承诺并不意味着要结婚”这句话有什么新的含义? 1)[[PromiseState]]。 有人还没有结婚。 2)[[PromiseResult]]。 因为他没有足够的钱参加婚礼。 3)[[PromiseRejectReactions]]。 结果,他有很多空闲时间花在了自我发展上4)[[PromiseFulfillReactions]]。 为什么一个人选择了计划A后就需要计划B?


是的,还有第五个字段[[PromiseIsHandled]]。 对于我们的员工来说,这不是很重要,将来我们将不再对其进行操作。 简而言之:在解释器中存储了一个信号,告知程序员是否拒绝了诺言。 如果不是,则JS引擎会将原始的Promise拒绝视为错误。 对于不耐烦的人:如果程序员没有将Promise.prototype.then()函数挂起为Promise被拒绝状态的第二个调用函数处理程序,则对象的“ Rejected” Promise状态将在开发人员控制台中向您显示红色错误。


您是否注意到诺言对象的字段包含在“ [[”和“]]”中? 这强调了JS程序员没有直接访问此信息的权限。 仅通过特殊工具/命令/ API,例如Promise.prototype.then()命令。 如果您有直接控制“这间厨房”的不可抗拒的欲望,欢迎来到EcmaScript规格标准俱乐部。


在本章末尾的简短说明。 如果在我们国家的生活中可以部分实现诺言,那么在EcmaScript中-不能。 也就是说,如果一个人答应捐献一百万,并捐出了95万,那么在生活中,也许他是一个可靠的伙伴,但是对于JavaScript来说,这样的债务人将通过[[PromiseState]] ===“被拒绝”列入黑名单。 Promise对象明确地更改其状态,并且仅更改一次。 在技​​术上如何实现这一点还需要一点时间。


设计师Promise,他的理念。 回调函数执行程序就像承诺的“执行程序”。 交互方案:承诺(构造函数)-执行程序(回调)-承诺(对象)


因此,我们发现promise从技术上来说是一个带有特殊隐藏内部字段的JS对象,这反过来又在哲学上提供了“承诺”一词的含义。


当初学者第一次创建一个Promise对象时,下面的图片在等待着他(图5)。


错误创建诺言对象
图5.(我们第一次直观地创建一个promise对象)

出了什么问题以及为什么该错误是一个标准问题。 在回答这个问题时,最好再次从生活中带来一些类比。 例如,很少有人喜欢我们周围的“空响”:他们只许诺,却无所作为以履行他们的要求(政治不算在内)。 在承诺后制定计划并立即采取行动以实现承诺结果的人们,我们的能力会更好。


因此,ECMAScript理念意味着,如果您创建了一个承诺,那么请立即表明您将如何实现它。 程序员需要以函数参数的形式制定其行动计划,然后将其传递给Promise构造函数。 下一个实验看起来像这样(图6)。


Promise构造函数使用执行程序
图6.(创建一个promise对象,将executor函数传递给Promise构造函数)

从标题到图,我们看到函数(Promise构造函数参数)有自己的名称-执行程序。 她的任务是开始兑现诺言,最好将其做出某种合乎逻辑的结论。 而且,如果程序员可以在执行程序中编写任何代码,那么程序员如何向JS发出信号,即工作已完成,您可以去查看承诺的结果吗?


帮助程序员通知承诺已完成的标记或信号会自动传递给执行者-参数以JavaScript专门形成的参数形式出现。 您可以根据需要调用这些参数,但是大多数情况下,您会使用res和rej之类的名称来满足它们。 在ECMAScript规范中,它们的全名是resolve function和reject function。 这些功能标记具有其自身的特征,我们将在稍后讨论。


为了了解新信息,请新来者对以下语句进行独立编码:“我保证,如果除数不为零,我可以将一个数除以另一个并给出答案。” 代码如下所示(图7)。


承诺任务:除以零
图7.(将数字除以promise的问题的解决方案)

现在您可以分析结果了。 我们看到浏览器控制台第二次以有趣的方式显示Promis对象。 即:2个附加字段用双方括号表示。 您可以安全地在[[PromiseState]]和[[PromiseStatus]]之间进行类比,并完成并解决[[PromiseValue]]和[[PromiseResult]]。 是的,浏览器本身试图告诉程序员Promise对象内部字段的存在和值。 我们还看到了Promise对象,执行程序函数,特殊函数-callback-tokens res和rej的连接系统。


为了使学生/伴侣在该材料上更加轻松,向他提供以下代码(图8)。 有必要对其进行分析并回答以下问题。


承诺任务:被零除。替代版本
图8.(将数字除以诺言的问题的解决方案的变化)

代码会工作吗? 执行器功能在哪里,它的名字是什么? 此代码中是否合适使用名称“ wantToDivide”? 绑定函数自身返回什么? 为什么参数仅在第二和第三位传递给bind函数? 特殊功能在哪里解析功能和拒绝功能消失了? 必要的输入数字1和2如何进入“承诺履行计划”? 参数伪数组中有多少个元素? 是否可以从内存中恢复浏览器控制台中的答案?


邀请读者自己思考问题的答案。 也一样
在代码中进行实验。 幸运的是,代码很小,任务的想法很简单。 是的,关于JavaScript的承诺和常识都存在疑问。 做什么,到处都是我们在等待令我们放松的惊喜。 一旦一切变得清晰起来,您就可以继续。


查看/复制代码
let number1 = Number(prompt("input number 1")); let number2 = Number(prompt("input number 2")); let wantToDivide = function() { if (arguments[1] === 0) { arguments[3]("it is forbidden to divide by zero"); return; } let result = arguments[0] / arguments[1]; arguments[2](result); }; let myPromise = new Promise(wantToDivide.bind(null, number1, number2)); console.log(myPromise); 


考虑执行者-一个参数:解析和拒绝函数


因此,我们喝了咖啡-我们继续前进。 让我们更详细地考虑特殊功能的resolve函数和拒绝函数,它们由JavaScript自动生成,将对象的承诺转换为实现或拒绝状态,这表示承诺的结束。


首先,让我们尝试在开发人员的控制台中简单地看一下它们(图9)。


研究解析功能
图9.(功能解析功能的研究-res)

我们看到resolve函数是一个带有一个参数的函数(属性长度=== 1)。 它的原型是Function.prototype。


好的,让我们继续实验。 如果我们从执行程序中删除到外部作用域的resolve /拒绝函数的链接,将会发生什么? 会破裂吗(图10)?


承诺对象的外部控制
图10.(我们将myPromise承诺转换为承诺之外的实现状态)

没有什么不寻常的。 JavaScript中作为对象的子种类的功能通过引用传递。 一切都按预期完成。 来自externalRes闭包的变量引用了我们的解析函数res。 我们使用其功能将承诺置于执行者本身之外的已实现状态。 下面的稍作修改的示例也显示了相同的想法,因此请看一下代码,然后思考myPromise1和myPromise2处于什么状态以及值为多少(图11)。 然后,您可以检查扰流板下的假设。


诺言任务。问题 图11.(反思的任务。诺言myPromise1和myPromise2处于什么状态和什么值将出现在开发人员的控制台中?)

问题的答案在图11(图12)中。
诺言任务。回答
图12.(图11中问题的答案)

现在您可以考虑一个有趣的问题。 但是,解析/拒绝功能如何始终准确地知道将哪个承诺转换为所需状态? 我们转向规范中算法,算法描述了如何创建这些功能(图13)。


创建解析功能
图13.(为一个特定的promise对象创建解析功能的功能)

要注意的重点:


  • 在创建“解析/拒绝”功能时,它们会牢固地附加到与之对应的唯一promise对象上
  • 解析/拒绝函数作为对象数据类型具有自己的隐藏字段[[Promise]]和[[AlreadyResolved]],它们为每个人提供了熟悉的直观逻辑: b)如果至少一次调用了solve或reject函数,则不能将promise转移到另一个状态。 该算法可以由下图表示(图14)。

    解决功能和承诺对象
    图14.(resolve函数和reject函数的隐藏函数字段)

    现在,将不再考虑使用来自隐藏字段的此信息的算法,因为它们很冗长且更复杂。 我们仍然需要在理论上和道德上为他们做好准备。 现在,我可以确认您的想法:“哇,结果多么简单。 可能会在对象的诺言的每个分辨率/分辨率下,检查“对象”标志{[[Value]]:false}。 如果将其设置为true,我们将停止将诺言转换为具有简单返回值的另一种状态的过程。” 是的-正是这样。 看来您可以正确回答以下问题而不会出现问题。 开发人员控制台将产生什么结果(图15)?


    具有链接解析功能和Promise对象的专业知识
    图15.(显示带有一个特定promise对象的resolve和拒绝功能之间关系的实验)

    根据ECMAScript规范创建承诺对象的算法


    考虑一下它诞生于世界的迷人时刻-一个成熟的承诺对象 (图16)。


    承诺在ecmascript中创建
    图16(根据EcmaScript规范创建promise对象的算法)

    查看时不会出现任何复杂的问题:

    • Promise构造函数必须在构造函数模式下调用,而不仅仅是函数调用
    • Promise构造函数需要执行程序函数
    • 创建具有特定隐藏字段的JavaScript对象
    • 用一些初始值初始化隐藏字段
    • 创建与Promise对象关联的解决和拒绝功能
    • 我们调用执行程序函数执行,传递已经生成的令牌,解析函数和拒绝函数作为参数
    • 如果在执行程序执行期间出现问题,请将我们的promise对象置于拒绝状态
    • 返回变量promise promise对象。

    我不知道您是否发现现在或现在以正常的同步模式执行函数执行程序算法,甚至在将某些内容写入Promise构造函数左侧的变量之前也是如此。 但是到了适当的时候,这成为了我的启示。


    由于我们谈到了同步和异步这一主题,因此以下代码供您“思考”或进行实验。 问题:看过程序员Dima的创作后,您能回答下面编码的游戏是什么意思吗?


     function randomInteger(min, max) { return Math.floor(min + Math.random() * (max + 1 - min)); } function game() { let guessCubeNumber = Number(prompt("Throw dice? Guess number?", 3)); console.log("throwing dice ... wait until it stop"); let gameState = new Promise(function(res, rej) { setTimeout(function() { let gottenNumberDice = randomInteger(1, 6); gottenNumberDice === guessCubeNumber ? res("you win!") : rej(`you loose. ${gottenNumberDice} points dropped on dice`); }, 3000); }); return gameState; } console.log(game()); 

    当然,这是模切辊的仿真。 用户可以猜测掉掉的号码吗? 看看有机异步setTimeout如何集成到同步执行器中-在我们的计划中,掷骰子并找出失败的数字。 如何以一种特殊的方式在开发人员控制台中解释结果(图17)?


    如果我们尝试查看承诺,直到多维数据集停止(代码中指示了3000毫秒),我们将看到承诺仍处于等待状态:游戏尚未结束,多维数据集尚未停止,没有遗漏任何数字。 如果我们尝试在多维数据集停止之后查看promise对象,则会看到非常具体的信息:用户是赢了(猜测数字)还是输了,以及为什么(实际上掉了什么数字)。


    承诺游戏-投掷骰子
    图17.(当执行程序函数中存在异步操作时,对象的promise状态)

    如果您对此示例感兴趣,或者想猜测翻转后的立方体的数量,则可以复制代码并进行实验。 敢!


    兑现诺言的承诺反应


    如您在图14中看到的,解决/解决对象的诺言的后果被标记为“ +反应”和“-反应”。 ECMAScript规范中这些词的正式术语是承诺反应。 假定在以下文章中将详细考虑此主题。 就目前而言,我们将注意力集中在什么是承诺反应是什么的一般概念上,以便该术语可以与该词及其技术执行的哲学含义正确关联。


    我们记得,诺言可能会产生后果,但可能不会。 结果是什么? 这是将在一段时间后执行的操作:在兑现诺言之后。 并且由于这是一个动作,其结果可以用普通的JavaScript函数表示。 成功解决promise(+反应)时,将执行某些功能; 其他功能-在承诺进入拒绝状态(-反应)的情况下。 从技术上讲,当调用Promise.prototype.then()方法时,这些函数(结果)将作为参数传递。


    因此,promise反应的重要部分是将来某个时候执行的异步操作。 Promise反应的第二个重要组成部分-这是在Promise.prototype.then()命令执行后返回的新创建的Promise。 这是因为后果会影响其他承诺。 例如,有一个买车的承诺,但只有在兑现了赚取一定数量的钱的承诺之后才能实现。 兑现了一个诺言-解决了结果-现在可以兑现第二个诺言。


    实际上,一个诺言反应会在一定时间间隔内将诺言相互绑定。 重要的是要记住,反应是自动处理的。 函数调用(解决承诺的后果)是由JS引擎而不是程序员进行的(图18)。 而且,由于反应与承诺对象(承诺)本身密切相关,因此可以合理地假设,承诺反应算法在其逻辑中使用其内部字段。 最好了解所有这些细微差别,以便能够有意识地基于promise控制异步逻辑。


    在then()方法中承诺反应
    图18.(解决promise的结果由then()方法中的回调函数记录。回调将由JS引擎自动异步调用)

    总结一下


    1)我们熟悉JavaScript中的承诺,它们的理念和技术执行。 所有这些都是使用对象的特殊内部Promise字段实现的:[[PromiseState]],[[PromiseValue]],[[PromiseFulFillReactions]],[[PromiseRejectReactions]]。


    2)程序员有机会通过执行程序函数履行其诺言,该函数作为参数传递给Promise构造函数。


    3)已实现或未实现承诺的边界通常由称为res和rej的代码中的特殊标记功能,解析功能和拒绝功能确定。 这些函数由JavaScript自动创建,并将参数传递给执行程序。


    4)resolve函数和拒绝函数始终具有一个与它们相关联的promise对象,以及一个公共的特殊字段{[[Value]]:false},这确保了promise仅被解析一次。


    5)[[PromiseFulFillReactions]]和[[PromiseRejectReactions]]是Promise对象的内部字段,用于存储解决Promise的后果,其中重要的一部分是通过对象的Promise.prototype.then()promise方法定义的自定义异步函数。


    聚苯乙烯


    本文是InSimpleWords小组视频会议的摘要。 这样的“视频课程”足够多了,仍然有笔记的材料。 另一个问题是,社区成员连续阅读有关承诺的文章是否会很有趣。 等待您的评论。

Source: https://habr.com/ru/post/zh-CN478938/


All Articles