2009-10-15 67 views
13
面条代码

我发现自己写了很多在Javascript意大利面条的时候我必须处理异步 应用(特别是与所有的数据必须通过JS获得 OpenSocial的代码打交道时) 。通常的模式是这样的:如何避免在Javascript

  1. 用户第一次登录到应用程序,获取他的数据。
  2. 对他的数据做A(例如通过向服务器发送请求来获得他的朋友)。
  3. 对此数据执行B操作(例如,将他的朋友发送给服务器进行一些处理)。
  4. 对他的数据做C(例如,检查服务器响应是否有效,以便我们可以做其他事情)。

请注意,此顺序执行路径(1 => 2 => 3 => 4)不适合异步。性质 Ajax,所以用户最终等待很长时间,代码变成一团糟,因为每个步骤 取决于以前的。

的代码示例:

gadgets.util.registerOnLoadHandler(setupUser()) 
... 
function setupUser() { 
    var req = [get data and setup request] 
    req.send(some_url, some_data, function(response) { getFriendsFor(response.user) }); 
} 

function getFriendsFor(user) { 
    var friends = [get friends from user] 
    var req = [setup request] 
    req.send(some_other_url, some_other_data, function(response { validateFriendsResponse(response.friends) }); 
} 

function validateFriendsResponse(friends) { 
    if (friends.valid()) 
    ... 
    loadCanvas(); 
} 

你可以看到每个函数依赖于前一个,更糟糕的是,它在 被称为一个特定的顺序是有用的。当用户在等待时添加诸如显示/隐藏加载 屏幕和其他噱头之类的东西时,情况会变得更糟。

你会如何去修复呢?

+6

煮它,直到它是人类 – 2009-10-15 15:41:55

+2

您的主要担心似乎是用户最终等待很长时间才能完成所有这些。为什么不在每次请求返回后向用户提供一些反馈,以便他们知道发生了什么。它可以像3阶段进度条一样简单,或者告诉用户发生了什么。 (你甚至可以用“reticulating splines”消息来形容meme apeal。) – 2009-10-15 16:18:26

+0

Sam:我现在用一个可爱的机器人拍着手臂显示加载屏幕,但是代码也让我担心,因为我知道我会有一些在几个月内阅读它的问题。 – 2009-10-16 16:06:29

回答

4

一种选择可能是有一个变量,显示当前状态,并有一个“控制器”功能,始终是AJAX回调函数。根据当前状态,控制器功能将调用下一个功能。为了简化控制器功能,我可能会存储函数序列以调用Javascript对象,因此所有控制器函数都是查找并传递给序列中的下一个函数。这种方法可能具有一个JavaScript对象始终是参数的功能(并包含所有,是由早期的AJAX调用返回的数据来促进

例:

var user = {}; 
var currentState = 1; 

var someFunction = function(user) {//stuff here that adds data to user via AJAX, advances currentState, and calls controllerFunction as callback}; 
var someOtherFunction = function(user) {//stuff here that does other things to user, advances currentState, and calls controllerFunction as callback} 

var functionSequence = {1:someFunction, 2:someOtherFunction} 

var controllerFunction = function() { 
    //retrieve function from functionSequence based on current state, and call it with user as parameter 
} 
1

构建您的JavaScript客户端与MVC架构

+1

javascriptMVC - 猜他们应该得到他们的链接在IE8上工作! – ScottE 2009-10-15 16:00:00

+1

我会将Backbone(http://documentcloud.github.com/backbone/)和Spine(http://spinejs.com/)添加为轻量级客户端MVC框架。 – jbandi 2011-12-04 13:40:30

1

处理显示,隐藏和状态特征的代码应该被提取到函数中。然后,为了避免“spagheiness”,一种解决方案是使用匿名函数内联。

function setupUser() { 
    var req = [get data and setup request] 
    req.send(some_url, some_data, function(response) { 
    var friends = [get friends from user] 
    var req = [setup request] 
    req.send(some_other_url, some_other_data, function(response {   
     if (friends.valid()) 
     ... 
     loadCanvas(); 
    }); 
    }); 
} 
+0

这就是我刚开始时所做的,但遗憾的是OpenSocial JS API太冗长了。你可以在这里看到你的例子中的第2-4行在OS中的样子:http://gist.github.com/211062。有些代码可以在函数中被抽象出来,但它仍然会有像疯子一样跳跃的问题。 – 2009-10-15 16:20:43

0

如果你希望你的函数能够独立运行的,装备异步那些与通用的回调,而不是调用行中的“下一个”功能。然后,正如JacobM所说,设立一个“控制器”,可以按顺序调用它们。我已经修改了你的下面的代码示例演示(注意,这还没有经过测试):

gadgets.util.registerOnLoadHandler(userSetupController()) 
... 
function setupUser(callback) { 
    var req = [get data and setup request] 
    req.send(some_url, some_data, function(response) { callback(response.user) }); 
} 

function getFriendsFor(user,callback) { 
    var friends = [get friends from user] 
    var req = [setup request] 
    req.send(some_other_url, some_other_data, function(response { callback(response.friends) }); 
} 

function validateFriendsResponse(friends) { 
    if (friends.valid()) 
    return true; 
    else 
    return false; 
} 

function userSetupController() { 
    setupUser(function(user){ 
     getFriendsFor(user,function(friends){ 
      if (validateFriendsResponse(friends)) { 
       loadCanvas(); 
      } else { 
       // don't load the canvas? 
      } 
     }); 
    }); 
} 

创建回调得到,如果你不熟悉这些有点复杂 - 这里有一个体面的解释:http://pixelpushing.net/2009/04/anonymous-function-callbacks/。如果你想变得更复杂(同样,正如JacobM所建议的那样),你可以编写一些自动处理的代码 - 给它一个函数列表,并按顺序执行它们,传递回调数据。方便,但可能会为你的需要矫枉过正。

2

可以控制这种事情就像Observer pattern面条。一些JavaScript框架具有此功能的即用型实现,例如Dojo's发布/订阅功能。问题的

1

部分原因是,你在想这是有三个往返所需要的服务器的四步过程。如果你真的认为这是一个单一的工作流程,并且用户最有可能做的事情,那么最好的加速就是在第一次互动中收集尽可能多的信息以减少往返次数。这可能包括允许用户选中一个表示她想要遵循此路径的框,因此您不必在步骤之间返回给用户,或者允许她输入对朋友名称的猜测,以便处理第一次,或第一次预加载名单。

如果这只是用户可能遵循的许多路径之一,那么您已经勾勒出代码的方式效果最佳;多次往返都是必需的,因为在每次交互中,你都会发现用户想要什么,而不同的答案会让你朝不同的方向发展。这是当你松散耦合的代码风格真正闪耀的时候。由于用户的行为正在推动活动,因此每个步骤看起来都与之前的行为没有关联。

所以,真正的问题是用户是否在决定你的代码去的方向开始有一个选择。如果不是,那么你想优化你知道(或强烈预测)交互行为的路径。另一方面,如果用户交互驱动流程,那么将步骤分离并对每个交互作出反应是正确的,但您会期望更多不同的可能性。