首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

用 continuation 开发复杂的 Web 应用程序(3)

用 continuation 开发复杂的 Web 应用程序(3)

关于事件驱动编程编程用户界面的事件驱动风格可以追溯到开始使用客户机 - 服务器架构的时候。它基于中央事件处理器以及在处理器上注册的大量事件句柄。每个句柄注册自己感兴趣的事件,当特定事件发生的时候,中央事件处理器会根据通知注册了事件的相关句柄。用户的交互状态在中央模块中维护,根据内部持有的当前状态,中央模块把引入的事件分派给注册的句柄。
大多数基于 Web 的互交是事件驱动编程的特例,在这里,界面显示被委托给 Web 浏览器,而不是由运行在用户工作站上的胖客户端可执行程序管理。虽然典型的胖客户端不允许用户驱动的功能(例如后向导航和克隆),但是 Web 浏览器支持甚至鼓励这类功能。当然,资源丰富的程序员已经找到了定制浏览器界面的方法(使用脚本代码),禁止这类操作,但是这样就形成了对不同浏览器的依赖性。
虽然 MVC 的事件驱动风格的编程有许多优势,但是它还造成业务功能分布到多个模块中,从而使它变得非常复杂,难以开发、理解、维护复杂程度合理的 Web 应用程序。虽然开发了许多 Web 开发框架(例如 Struts、Spring、以及 JavaServer Faces)来隐藏大多数 MVC 风格的界面背后的复杂结构,但是有一些开发人员已经开始认识到这样的事实:其他编程模型值得深入研究。
continuation 的适用场合基于 continuation 的 Web 应用程序巧妙地避开了上面提到的与 Web 应用程序开发有关的问题。比起基于 MVC 的 Web 应用程序,基于 continuation 的应用程序是作为一个程序编写的。每当程序需要从用户得到输入的时候,就会把包含相应表单的 Web 页面发送回用户的浏览器,并生成代表应用程序逻辑
剩余部分的 continuation,并把它放在一边。(我很快就向您解释把 continuation "放在一边" 的选项)。因为重要的事是在接收到用户的响应时能够重新开始应用程序逻辑的剩余部分,所以还要为服务器生成了一个惟一的
id,作为在
continuation 存储库中搜索特定 continuation 的键。这个
id用某种适当的方式和显示给用户的页面一起也向后发送,这样表单提交会使
id在响应中发送回来。

还记得阻塞 I/O 吗?本文中描述的方法在一个典型的读写程序中使用(或者与所用语言中的
read和
write等价)continuation。概括来说:对read的调用使应用程序阻塞,只有在用户提供了输入之后才继续。提供的输入被用来继续应用程序逻辑。任何中间输出则通过非阻塞的
write调用显示给用户。下次程序要求用户输入信息的时候,它会进行另外一个read调用,并再次阻塞。这个序列持续进行,直到程序结束。
重要的是要注意:基于 continuation 的程序与使用阻塞读写的独立程序没什么不同。所有通常的编程结构都可以使用,包括基于
if条件的分支、
for和while循环,甚至更多。惟一的区别是:在 MVC 模型中,使用这些结构的应用程序代码有可能分散到多个不同模块和页面中,但是在基于 continuation 的程序中,整个逻辑包含在一个程序中。

当必要的响应从用户到达,服务器(为部署的 Web 应用程序提供 continuation 的基础设施)在响应中检索 continuation 的
id(与正常提交的数据一起),并用这个
id在存储库中检索 continuation 。然后就继续执行 continuation(也就是说,应用程序逻辑在紧接着创建 continuation 的代码之后开始执行)。通常,前几行会提取用户提交的数据的剩余部分,这意味着在程序中服务器必须能够访问请求对象,而业务逻辑会用接收到的数据继续进行。

在继续执行 continuation 时,启动执行的代码可能要求用户输入更多数据,所以需要进一步交互。处理方法是创建第二个 continuation,把它保存在 continuation 存储库,并把嵌入了适当 continuation
id的表单发送给用户。可以把生成的
外部(outer)
内部(inner)continuation 看作一个树结构,外部 continuation 作为父节点,内部 continuation 作为子节点。与此类似,如果业务逻辑有条件分支代码(根据
if子句的结果有不同的动作),针对每个分支发送回浏览器的数据搜集页面可能是不同的,因此会针对每个分支形成对应的 continuation,每个 continuation 都是外部 continuation 的子节点,而彼此之间则是兄弟节点。按照这种方式,可以把完整的应用程序看成是与 continuation 树对应 ——
continuation 的森林

以用户为中心的导航从外部来看,无法区分基于 continuation 的方法和 MVC 架构。客户仍然可以自由地返回前面已经提交的 Web 页面,修改必要的数据,然后通过浏览器再次提交表单。区别在于内部,在于为了让导航正确工作需要耗费的代码数量。基于 continuation 的方法不需要额外费力处理正确导航,因为每个提交的表单,都会有一个 continuation
id与之关联。服务器只需要查看某个提交页面的正确 continuation,就可以要求它继续执行。

假设用户正在查看页面。按照所谓的
continuation 树的术语来说,这个页面用某个 "continuation 节点" 进行了 " 标记" 。如果用户单击 Back 按钮返回到前面已经提交的页面,那么 continuation 树中的标识器就会向上移动一级,并把指针设置到这个节点的父结点。每次用户单击 Back 时,continuation 树中的移动就会发生一次。现在假设用户停在某个页面上,重新输入数据,新输入的数据可能与以前在这个页面中输入的数据相同,也可能不同,然后用户重新提交表单。这使 continuation 树中的标识器向下移动一级,指向子节点。但是,因为应用程序逻辑根据新提交的数据可能要显示不同的页面,所以现在的子节点实际可能是上一次移动中标识器指向的子节点的兄弟节点。按照同样的逻辑继续执行,在树中,后退的路径可能会遇到不同的一套 continuation,也可能遇到相同的,具体情况取决于用户提交的数据。
返回列表