A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

在JavaScript程序中,异步出现在诸如基于Web的用户界面,通过HTTP请求与服务器通信以及非阻塞I / O的情况下。基于事件的编程是管理异步的最流行的方法,但是遇到诸如丢失事件和事件竞争之类的问题,并且导致代码难以理解和调试。最近,ECMAScript 6增加了对promises的支持,promises是一种管理异步的替代机制,它使程序员能够在支持正确的错误处理的同时链接异步计算。但是,承诺本身就很复杂且容易出错,因此程序员可以从可以推断基于承诺的代码的正确性的技术中受益。
由于ECMAScript 6规范是非正式的,旨在用于JavaScript引擎的实现者,因此它不能为正式推理提供合适的基础。本文介绍了λp,这是一个核心微积分,它捕捉了ECMAScript 6承诺的精髓。基于λp,我们介绍了promise图,这是一个程序表示,可以帮助程序员调试基于promise的代码。然后,我们报告一个案例研究,其中我们研究了promise图如何有助于调试发布到StackOverflow网站的代码片段中与promises相关的错误。
CCS概念:•计算理论→操作语义;程序推理; •软件及其工程→面向对象的语言;
1 引言
异步控制流在JavaScript社区中广泛用于各种任务,例如实现基于Web的用户界面,通过HTTP请求与服务器通信以及非阻塞I / O.在JavaScript应用程序中容纳异步的最流行的方法是基于事件的编程。在此编程模型中,回调函数与特定对象上的特定类型的事件相关联。通常响应于一些外部活动发出事件,例如,用户点击按钮,或者到达网络响应,并且排队以进行处理。在主事件循环中,从队列中选择一个事件并调用并关联其相关的回调,直到完成。不幸的是,基于事件的JavaScript编程导致了高度复杂和深度嵌套的控制流,有时也被称为“回调地狱”。由于非显而易见的控制流和缺乏对错误处理的支持,这种代码非常容易出错。特别是,错误不会在两者之间传播。此外,可能会出现特定于事件驱动编程模型的几种类型的错误,例如丢失事件(在处理程序注册之前发出事件的情况)和死侦听器(事件处理程序从未执行的情况,因为处理程序注册太晚或错误的对象)[Madsen et al。[ 2015],以及事件竞赛错误,即程序行为根据事件处理程序被安排执行的顺序而非确定性地失败的情况[Adamsen et al。2017年;洪等人。2014;詹森等人。2015年; Mutlu等人。2015年;彼得罗夫等人。 2012; Raychev等人。2013;张和王2017;郑等人。 011]。
为了应对这些问题,JavaScript社区采用了promises,一种异步计算的编程模型,最初由Friedman和Wise [1976]提出。 promise表示异步计算的值,并且处于三种状态之一(待处理,已履行或已拒绝)。 Promise配备了两个函数,resolve和reject,分别用于解析或拒绝具有特定值的promise。 then操作创建一个新的promise,其值取决于原始promise的解析或拒绝方式。这使程序员能够将异步计算链接起来,并将错误从一个异步执行的函数传播到另一个异步执行的函数。已经以库的形式开发了几种promises实现,包括BlueBird [Antonov 2013]和Q [Kowal 2010],并且诸如jQuery之类的流行框架也提供了promise的实现。最近,ECMAScript 6引入了承诺的标准实施,已经获得认可[ECMA 2015,第25.4节]。但是,JavaScript承诺的语义非常复杂,并且由于该功能是通过普通函数调用实现的,因此没有静态检查来确保正确使用。因此,程序员经常会在基于承诺的代码中犯错,从而导致恶意错误,这可以从诸如像这StackOverflow等论坛上的许多报告问题中看出。本文的主要贡献之一是对JavaScript承诺相关的常见错误类型进行分类。
我们研究的长期目标是开发用于检测基于承诺的JavaScript代码中的错误的工具。但是,ECMAScript 6标准非正式地并且在操作方面规定了承诺的语义,并且不是正式推理或程序分析的合适基础。本文通过提出λp来克服这一障碍,这是一种微积分,其中承诺的行为表示为λJS的延伸[Guha et al。 2010]。 λp演算反映了ECMAScript 6承诺的行为,并包括其最重要的特征,同时省略了不必要和分散注意力的细节。基于λp,我们将promise图作为程序工件引入,以帮助理解和调试基于承诺的程序。我们通过报告案例研究来证明promise图的有用性,在案例研究中我们研究了如何使用promise图来理解StackOverflow上报告的与promise相关的问题。
总之,本文做出以下贡献:
•与JavaScript承诺相关的常见编程错误的分类。
•λp演算,为JavaScript承诺的子集提供形式语义。
•promise图,一个程序工件,可用于检测与promise相关的错误。
2 动机
本节简要介绍了JavaScript承诺,讨论了有关promises的一些常见问题,并提供了一个示例,说明了使用它们的JavaScript程序中可能出现的错误类型。
2.1 JavaScript承诺简介:promise是一个表示异步操作结果的对象。承诺始终处于以下三种状态之一:
待定:异步操作尚未完成,且promise没有任何值。这是每个承诺的初始状态。
已实现:异步操作已成功,并且promise包含结果值。
拒绝:异步操作失败,promise包含错误值。如果承诺得到满足或拒绝,则承诺得到解决。
使用Promise构造函数构造Promise,该构造函数使用带有两个参数的单个回调函数。此函数的参数是两个函数(通常在惯用JavaScript中称为resolve和reject),必须分别调用它们来解析或拒绝promise。例如,图1(a)中的代码片段创建了一个使用值42立即解析的promise。一旦promise被解决,后续的解析或拒绝调用不会影响promise的状态。
then函数可用于记录解决反应并拒绝承诺的反应。这些反应是普通函数,它们分别在与它们相关联的约束被解析或拒绝时在事件循环中调用。然而,then函数不仅仅通过创建依赖的promise来执行相关的解析反应或拒绝反应,该依赖的promise通过解析(拒绝)反应计算的结果被解析(或拒绝)。这使得能够构造一系列承诺,其中计算值或误差值从一个计算传递到下一个计算。例如,图1(b)中的代码首先创建一个使用值42解析的promise。随后,解析反应与此promise相关联,该promise将打印此值并创建一个使用值84解析的依赖promise。
promise p2将以值19解析。如果p1被拒绝且值为18,则将执行拒绝反应,导致打印“error”,并且依赖的promise p2将使用值20.2解析。函数可用于构造形式的承诺链:p1.then(...,...).. then(...,...)。then(...,...)。
与事件驱动的编程相比,promises的使用有几个优点:
•与事件处理程序不同,当在承诺已经解决之后创建与承诺的关联时,仍会安排执行反应。这可以防止事件驱动程序中可能出现的“丢失事件”错误[Madsen et al。 2015年]。
•在承诺链中,可以将错误值(例如,异常,错误值或错误消息)从承诺传播到依赖承诺。相反,在事件驱动的程序中,异常传播到顶层,并且不能传播到随后执行的事件处理程序。
•承诺的链接导致代码,其中随后执行的反应按顺序出现在同一级别。相比之下,传统的事件驱动编程导致深层嵌套的代码难以理解,这种情况有时被称为“回调地狱”或“厄运金字塔”。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马