了解全栈JavaScript中的异步交互外文翻译资料
2021-12-13 22:27:36
英语原文共 12 页
了解全栈JavaScript中的异步交互
摘要
JavaScript已成为实践中最流行的语言之一。开发人员现在不仅将JavaScript用于客户端,还用于服务器端编程,从而导致完全用JavaScript编写的“全栈”应用程序。由于异步和事件驱动实体在客户端和服务器端的时间和隐式关系,了解此类应用程序对开发人员来说具有挑战性。我们提出了一种捕获全栈JavaScript应用程序执行行为模型的技术。该模型是时间和上下文敏感的,以适应异步事件,以及回调生命线的调度和执行。我们提供了模型的可视化,以便于开发人员了解程序。我们在一个名为Sahand的工具中实现我们的方法,并通过受控实验对其进行评估。结果表明,Sahand通过将准确度提高三倍来提高开发人员完成项目理解任务的能力。
类别和主题描述:
D.2.5 [软件工程]:测试和调试跟踪;
D.2.7 [软件工程]:分发,维护和增强
一般用语:设计,算法,实验
关键词:程序理解,异步性,全栈JavaScript
一、引言
JavaScript连续三年被选为最受欢迎的编程语言[42],它是GitHub上最常用的语言[23]。对于某些人来说,JavaScript一直是客户端Web开发的通用语言。
多年来,JavaScript一直是客户端Web开发的通用语言。 但是像Node.js这样的平台[34]已经可以使用JavaScript来编写在浏览器之外运行的代码。 因此,从客户端到服务器端完全用JavaScript编写的“全栈”应用程序最近也出现了指数级增长。Node.js为编写基于网络的应用程序提供了轻量级,无阻塞,快速且可扩展的平台。Web开发人员使用相同的语言进行前端和后端开发也更方便。尽管具有所有优点,但这种方法对开发人员理解Web应用程序的动态执行提出了许多挑战。了解应用程序是几乎所有软件工程任务的必要的第一步。
理解客户端,服务器端的执行及其交互涉及三组挑战。首先,JavaScript是一种单线程语言,因此通常会使用回调来模拟并发。定期使用嵌套和异步回调[16]来提供非阻塞I/O和并发请求处理等功能。然而,这种回调的使用会严重地使程序理解和维护变得复杂——这是开发人员在网络上被称为“回调地狱”的问题。其次,文档对象模型(DOM)和自定义事件,定时器和XMLHttpRequest(XHR)对象与客户端和服务器上的JavaScript代码交互以提供实时交互,所有这些都使理解变得复杂。此外,Node.js部署事件循环模型来处理和调度异步事件和回调,不正确的使用会导致应用程序的意外行为。最后,客户端和服务器代码通过XHR消息进行通信,并且多个消息(及其响应)可以在给定时间传输。与任何分布式系统一样,无法保证服务器上的请求到达的顺序或时间,以及客户端的响应。异步通信中涉及的不确定性使得执行更复杂,因此更难以理解开发人员。
尽管JavaScript的普及和这些挑战的严重性,目前还没有可用的技术提供全栈Web应用程序中JavaScript代码执行的整体概述。现有技术不支持全栈JavaScript理解[4,18,27,35,47]。在我们之前的工作中,我们提出了一种名为Clematis [3]的技术,用于理解客户端JavaScript。 但是,Clematis仅适用于客户端JavaScript,并且与服务器无关,其中大多数程序逻辑位于全栈应用程序中。
在本文中,我们提出了一种名为Sahand的技术,以帮助开发人员全面了解全栈JavaScript应用程序的动态行为。 我们的工作做出了以下贡献。
bull; 我们提出了一种全新的JavaScript应用程序的时间和行为模型。该模型具有上下文敏感性,可在客户端和服务器端创建JavaScript执行的生命线。该模型通过其异步通信连接双方,以提供应用程序行为的整体视图。
bull; 我们创建了一个可视化界面,用于向开发人员显示模型,以帮助他们理解执行的基本机制。我们将模型视为多变量时间序列,基于此,我们创建了JavaScript执行生命线的时间可视化。
bull; 我们在一个名为Sahand的工具中实现我们的方法[39]。该工具独立于浏览器且不具有侵入性。 Sahand可以处理Java-的模拟并发
通过异步执行回调,XHR对象,定时器和事件来编写脚本。
bull; 我们通过与12名参与者进行的对照实验来评估我们的方法。我们的结果表明,使用Sahand可以帮助开发人员更准确地执行三次程序理解任务。
二、挑战和动机
要理解全栈Web应用程序的行为,必须了解客户端和服务器端的功能的整个生命周期。 我们详细说明了图1-3中所示示例中涉及的一些挑战。这些都是简单的例子,挑战在大型和复杂的应用程序中更有效。
2.1 挑战1:服务器端回调
在端点接收请求。在服务器上的端点处接收各种类型的HTTP请求。 Node.js应用程序具有分配给每个传入请求的一个或多个处理程序。每个处理程序都可以更改执行流程,返回到客户端,或将执行传递给下一个处理程序。注册匿名函数或函数数组的能力可能使理解和管理请求的过程复杂化。
实例。图1的示例描绘了用于接收GET请求的端点(第12-18行使用Express.Js API [14])。注册三个项目作为/ locate请求的处理程序。首先,注册匿名函数(第12-15行),它可以有条件地返回对客户端的响应(第13-14行)并阻止执行剩余的处理程序。第二个分配的处理程序(第16行)是一个回调函数cb1()和cb2()(第8行)的数组。可以基于动态信息在运行时将附加函数cb0()推送到数组(第9-11行)。 cb0()本身可以控制流程并在特定场景中向客户端发送响应(第3-4行)。最后,另一个匿名函数被添加到请求处理程序列表中(第16行)。所有这些交易者的复杂流程取决于它的理解方式。随着处理程序数量的增加,这项任务将变得更具挑战性。回调函数。函数是java脚本中的一等公民。
1 |
var cb0 = function (req, res, next) { |
|
2 |
var region = locateClient(req.body.client) |
|
3 |
if (region.ASIA) { |
|
4 |
res.send(customizedRes(req.body.content)) |
|
5 |
} |
|
6 |
next() |
|
7 |
} |
|
8 |
var cbacks = cb1, cb2 |
|
9 |
if (user.isLoggedIn) { |
|
10 |
cbacks.push(cb0); |
|
11 |
} |
|
12 |
app.get( #39;/locate #39;, function(req, res, next) { |
|
13 |
if (req.header( #39;appStats #39;)) |
|
14 |
res.send(statCollectionResponse(req.body. stats)) |
|
15 |
next(); |
|
16 |
}, |
cbacks, function(req, res) { |
17 |
// do stuff |
|
18 |
}) |
图1:在端点接收HTTP请求
extractArgs(row, function (instType) {
|
图2:回调函数
它们可以作为其他函数的参数给出,并在以后执行。回调函数广泛用于JavaScript应用程序[16]。但是,理解部署回调的JavaScript代码并非易事。许多情况下回调是嵌套的(最多八级[16])或循环分配,这会对读者跟踪数据和控制流的能力产生负面影响。这个问题被开发人员称为回调地狱[8]。为了加剧这种情况,Node.js部署了用于调度和组织回调的事件循环模型。事件循环对开发人员不可见,但它确定了服务器端的异步执行。
例如,图2中的代码描述了一个简单的回调函数示例。许多回调函数以嵌套方式作为参数传递给其他函数(第2-5行)。因此可以在循环中分配回调。在我们的示例(第3-5行)的情况下,相同的匿名函数被指定为循环的所有迭代的回调。
2.2 挑战2:异步客户端
通常在客户端使用两个异步事件。首先,异步XHR消息用于与服务器无缝通信而不改变状态。其次,定时事件用于执行周期性任务,或者在时间延迟之后发生的任务。要处理异步事件,通常使用在事件发生时触发的回调。 但是,原始功能对开发人员来说是一项具有挑战性的任务,当源和回调通常在语义上和时间上分开时尤其如此。
例如,图3中的示例代码显示了一个简化的客户端JavaScript代码。updateUnits()函数(第1行)在循环中向服务器发布一组XHR请求(第2-7行)。 这些消息中的每一个都有一个回调函数,该函数在收到服务器的响应时被调用。
资料编号:[5411]
1 |
function updateUnits() { |
2 |
for (var i = 0; i lt; unit.length; i ) { |
3 |
(function(i) { |
4 |
$.post(extractUrl(i), function(data) { |
5 |
if (data.requiresAlert()) |
6 |
setTimeout(extractMessage(data), msgDelay); |
7 |
}); |
8 |
} } })(i); |
9 |
function periodicUpdate() { |
10 |
$.get( #39;/pupdate #39;, function(data) { |
11 |
// do stuff |
12 |
}); |
13 |
} |
14 |
setInterval(periodicUpdate, updateCycle); |