Douglas Crockford 大神写的 JavaScript 异步控制库:RQ(上)

RQ

Douglas Crockford
2015-10-06

翻译 http://blog.csdn.net/zhangxin09

RQ 是一个运行在服务端用于管理异步的小型 JavaScript 库。

RQ is a small JavaScript library for managing asynchronicity in server applications.

源码在 https://github.com/douglascrockford/RQ。本页在 http://www.RQ.crockford.com/。采用公有领域。

The source is available at https://github.com/douglascrockford/RQ. This page is available at http://www.RQ.crockford.com/. It is in the Public Domain.

异步 Asynchronicity

无论用户界面还是服务端,异步渐成为解决繁复问题的一种良好手段。异步函数会尽快地把控制权交到调用者手中。调用者通常会有片刻的等待,才会晓得通讯成功或失败。通讯过程可能会使用到回调函数或者延续(continuation)。

Asynchronicity is becoming the preferred method for solving a large class of problems, from user interfaces to servers. Asynchronous functions return control to the caller almost immediately. Success or failure will be communicated somehow in the future, but usually the caller will resume long before that occurs. The communication will probably make use of some sort of callback function or continuation.

最简单来说,旧方式写法

At its simplest, it means that instead of writing

function oldway( . . . ) {
    . . .
    return result;
}

将会改为现在的,

we now sometimes write

function newway(callback, . . . ) {
    . . .
    return callback(result);
}

然而,这不是一句话两句话就能说完的。

But of course it is not that simple.

服务端所进行的工作流,往往与前端的有极大不同。一次请求可能会触发多个进程,当中完成了一个进程后就把交给下一个进程。也就是说,每个步骤可能依靠其他进程来处理,所以必须依靠回调来返回结果。等待的同时不会阻塞程序。原始的方法是,在前一个的回调函数中调用下一步。但通常这种程序写起来比较麻烦,难以阅读,也难以维护。

Servers offer workflows that are significantly different than those found in user interfaces. An incoming message might require several processing steps in which the result of each step is given to the next step. Since each step may be concluded in a different processing turn, callbacks must be used. The program may not block while awaiting results. The naïve approach is start each step in the callback function of the previous step. This is a very awkward way to write, producing programs that are brittle, difficult to read, and difficult to maintain.

一个流程可以分为若干独立的步骤,意味着不但可以并行运行这些步骤,而且更重要的结果是会快很多。整体所消耗的时间仅仅是最满那次步骤之时间,非所有步骤所累加的时间——显然那会快很多。但是如果用简单的回调则不容易发挥并行的优点。原始的方法是,连续地执行每个步骤。但通常这种程序写起来比较麻烦,难以阅读,也难以维护。

A workflow might have several independent steps, which means that those steps could be taken in parallel, which could have significant performance benefits. The elapsed time of the steps could be the slowest of all of the steps, instead of the total of all of the steps, which could be a dramatic speed up. But it is not obvious how to take advantage of parallelism with simple callbacks. The naïve approach is to perform each step serially. This is a very awkward way to write, producing programs that a brittle, difficult to read, difficult to maintain, and much too slow.

该并行模式是如此的困难以至于某些用户吐槽异步这玩意搞起来不大自然(或曰“反人类”)。但要搞清楚的是这不是异步本身的问题(异步其实是极好的),只不过我们没有适当的工具来异步罢了。尽管已经有了诸如 promise 等的工具来搞定那些问题,但 promise 本身却不是为服务端而设计的。

This pattern is so problematic that some of its users have denounced asynchronicity, declaring that it is unnatural and impossible to manage. But it turns out that the problem isn't with asynchronicity. The problem is trying to do asynchronicity without proper tools. There are lots of tools available now, including promises. There are many good things that can be done with promises, but promises were not designed to help manage workflows in servers.

于是,发明 RQ 就有了合理的解释了。异步是好东西。我们不应该无视或拒绝它。我们应该尽量接受它,因为它是大势所趋。而 RQ 就赋予了你这么一个简单的工具实现异步。

That is specifically what RQ was designed to do. Asynchronicity is our friend. We should not attempt to hide it or deny it. We must embrace asynchronicity because it is our destiny. RQ gives you the simple tools you need to do that.

工作流 Workflow

RQ 中的一个完整工作流可以分解成为若干的步骤(或称作任务或工作)。每个步骤视为函数。这些函数又可以称为“请求者(requestors)”, 因为调用中很可能会发起请求。RQ 的作用在于统筹这些请求者们,采用串行或并行的方式来处理它们。

With RQ, a workflow is broken into a set of steps or tasks or jobs. Each step is represented by a function. These functions are called requestors because calling one is likely to initiate a request. RQ provides services that can collect some requestors together and process them sequentially or in parallel.

举个例子,respond 请求者首先会调用 getId,得到结果后把结果交给 getPreference 请求者,然后又把得到的结果交割 getStuff 请求者。最后一个 请求者 buildPage 会把最后一个结果 stuff 用于构建页面。

For example, the respond requestor will call the getId requestor, give that result to the getPreference requestor, and give that result to the getStuff requestor. The buildPage requestor will use the stuff to build a page.

respond = RQ.sequence([
    getId,
    getPreference,
    getStuff,
    buildPage,
]);

上述使用的 getStuff 请求者会返回 stuff 数组,所谓 stuff 数组里面的内容又是并行方式获取的。发出的请求有 getNav、getAds、getWeathergetMessageOfTheDay  这一组,并也同时请求 getHoroscopegetGossip 这一组。但是这两组它们有什么不同呢?后一组被视为不要紧的,可有可无的:如果前一组搞定的话而后一组未搞定的话,则不管后一组怎么样不会等待,就视为全部搞定。反过来说,如果后一组在前一组成功的时限内,那么后一组将会有效。

The getStuff requestor that was used above will produce an array of stuff, and it can get all of the stuff in parallel. It will begin the getNav, getAds, getWeather, and getMessageOfTheDay jobs, and also begin getHoroscope and getGossip. Those last two are considered unimportant. If they can be finished before the four main jobs finish, then they will be included in the result. But we won't wait for them.

getStuff = RQ.parallel([
    getNav,
    getAds,
    getWeather,
    getMessageOfTheDay
], [
    getHoroscope,
    getGossip
]);

RQ 提供竞赛机制的支持。所谓竞赛,表示一组步骤中,以最快完成的那个为胜利者,取胜利者的结果,其余的不理。我们上述使用的 getAds 请求者实际是这样的。

RQ can also support races, where several jobs are started at once and the first one to finish successfully wins. We could have created the getAds requestor that was used above like this:

getAds = RQ.race([
    getAd(adnet.klikHaus),
    getAd(adnet.inUFace),
    getAd(adnet.trackPipe)
]);

getAd 是生产请求者的工厂函数。它有个有一个表示最快广告网络的参数。在这个例子中会发起对这三个广告网络的请求,看哪个最快响应的便是结果。

getAd is a factory function that makes requestors. getAd takes a parameter that identifies an advertising network. In this example, requests will be made of three advertising networks. The first to provide an acceptable response will win.

RQ 亦支持“备胎(fallback)机制”。倘若 A 计划失败则尝试 B 计划;B 计划失败的话,则 C 计划上。上述所用的 getWeather 请求者即是一个“备胎函数”。首先尝试从本地缓存获取结果;倘若失败则从本地数据库获取;最后也不行的话,则尝试从远程数据库获取天气。

RQ can also support fallbacks. If Plan A fails, try Plan B. And if that fails, Plan C. The getWeather requestor that was used above could have been made as a fallback. It would first try to get a result from the local cache. If that fails, it will try the local database. If that fails, it will try the remote database.

getWeather = RQ.fallback([
    fetch("weather", localCache),
    fetch("weather", localDB),
    fetch("weather", remoteDB)
]);

小结下,RQ 有四种函数:串行函数 RQ.sequence、并行函数 RQ.parallel、 竞争函数 RQ.race 和备胎函数 RQ.fallback。每种函数都可以传入请求者组成的数组,然后返回一个总的请求者。这个所谓的总的请求者就是企图把多个请求者函数合并为一个。每个函数可传入超时函数,一旦超时了则取消任务。

RQ provides just four functions: RQ.sequence, RQ.parallel, RQ.race, and RQ.fallback. Each takes an array of requestors and returns a requestor that combines them into a unit. Each can also take an optional time limit which can cancel the jobs and produce an early failure if time runs out.

时间: 2024-10-17 03:39:14

Douglas Crockford 大神写的 JavaScript 异步控制库:RQ(上)的相关文章

Douglas Crockford 大神写的 JavaScript 异步控制库:RQ(下)

RQ 库 The RQ Library RQ 仅仅是一个文件,rq.js.运行起来创建一个 RQ 变量,该变量包含四个函数. RQ is delivered as a single file, rq.js. When run, it produces an RQ variable containing an object containing four functions. RQ.sequence(requestors) RQ.sequence(requestors, milliseconds)

请看一个大神写的lambda表达式,不用linq请问怎么调用

问题描述 请看一个大神写的lambda表达式,不用linq请问怎么调用 Func<int, bool> MyAnd(Func<int, bool> f1, Func<int, bool> f2) { return x => f1(x) && f2(x); } 解决方案 直接调用也可以: bool b1 = MyAnd(x => x < 10, x => x % 2 == 0)(4); bool b2 = MyAnd(x =>

c++基础c++-求大神写一段c++代码,做题能做对但是自己写代码就漏洞百出,求大神指导

问题描述 求大神写一段c++代码,做题能做对但是自己写代码就漏洞百出,求大神指导 年龄 Age姓名 char name公有成员函数: 构造函数 带参数的构造函数Student(int mchar); 不带参数的构造函数 Student() 析构函数 -Student() 改变数据成员值函数 void SetMemer(int mchar *) 获取数据成员函数 int GetAge() char * GetName()要求:在main()中定义一个有3个元素的对象数组并分别初始化,然后输出对象数

应用-麻烦大神写出一个c++程序出来

问题描述 麻烦大神写出一个c++程序出来 几何图形的简单计算.内容是用C++设计一个业务处理系统,其业务就是进行简单的几何计算:l输入圆的半径,计算并输出圆的周长和面积:l输入圆柱的半径和高,计算并输出圆柱的表面积和体积:l输入圆锥的半径和高,计算并输出圆锥的表面积和体积:l输入圆球的半径,计算并输出圆球的表面积和体积:l输入长方形的长和宽,计算并输出长方形的周长和面积:l输入长方体的长.宽和高,计算并输出长方体的表面积和体积.要求:①必须设计圆.圆柱.圆锥.圆球.长方形.长方体等类:②必须应用

求大神解答-求大神写一下这个SQL语句,紧急

问题描述 求大神写一下这个SQL语句,紧急 表acct_item(acct_item_id,acct_id,acct_item_type_id,charge(费用:单位分)).查询acct_id总费用大于100元的合同号,按总费用降序,总费用相同时,按acct_id升序排列 解决方案 select acct_id from acct_item where charge>'10000' order by charge desc,acct_id desc 解决方案二: select acct_id

求大神写个读取SQLserver数据库显示柱状图的代码,跪求

问题描述 求大神写个读取SQLserver数据库显示柱状图的代码,跪求 解决方案 解决方案二:你sql读取信息,然后在柱状图上绑定对应的字段就可以了.解决方案三:.NET3.5以上的vs项目,可以使用Chart控件,工具箱--->数据,然后在属性里指定DataSourceID,根据向导完成即可,中间要建立DataSource,指定表,字段等解决方案四:Sql只是提供数据,至于显示数据的方式,都是展示层的控件来决定的.就像楼上说的chart控件或者网上许多的第三方控件解决方案五:HChartECh

php-怎样删除二维数组中相同的一位数组 并保持相同键名 求大神写个函数

问题描述 怎样删除二维数组中相同的一位数组 并保持相同键名 求大神写个函数 Array ( [0] => Array ( [year] => 2013-2014 [term] => 1 [course_code] => 00008069 [course_name] => 咖啡世界 [course_nature] => 任意选修 [course_attribution] => 人文素养类 [credit] => 2.0 [point] => 4.1 [g

github-求大神解答Eclipse中怎样导入GitHub上的项目?

问题描述 求大神解答Eclipse中怎样导入GitHub上的项目? 折腾了一下午,在百度上知乎上找了N久,通过URL导入和本地文件导入终究还是没有解决问题,比如下载了下面的这样一个小项目,解压后如图示目录,该如何是好? 解决方案 你这个github项目作者明显是用android studio写的,gradle是证实,还引用的maven,pom.xml是证实,并且你这个github项目还是带有第三方lib的,所以普通的导入肯定是有问题的,需要修改gradle配置,具体要看你的报错信息,同时如果你想

iterator-那个大神帮我更改成可实现上传3张图片的

问题描述 那个大神帮我更改成可实现上传3张图片的 <%@ page language="java" import="java.util.*,java.sql.*" pageEncoding="utf-8"%> <%@page import="java.io.File"%> <%@page import="cn.to8ge.entity.*"%> <%@page im