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

简介: RQ Douglas Crockford 2015-10-06 翻译 http://blog.csdn.net/zhangxin09 RQ 是一个运行在服务端用于管理异步的小型 JavaScript 库。

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.

目录
相关文章
|
JavaScript 前端开发 Java
通义灵码 Rules 库合集来了,覆盖Java、TypeScript、Python、Go、JavaScript 等
通义灵码新上的外挂 Project Rules 获得了开发者的一致好评:最小成本适配我的开发风格、相当把团队经验沉淀下来,是个很好功能……
2057 103
|
Web App开发 JavaScript 前端开发
如何在JavaScript中确定异步操作之间的依赖关系?
如何在JavaScript中确定异步操作之间的依赖关系?
533 156
|
JavaScript 前端开发 API
|
资源调度 JavaScript 前端开发
Day.js极简轻易快速2kB的JavaScript库-替代Moment.js
dayjs是一个极简快速2kB的JavaScript库,可以为浏览器处理解析、验证、操作和显示日期和时间,它的设计目标是提供一个简单、快速且功能强大的日期处理工具,同时保持极小的体积(仅 2KB 左右)。
830 24
|
数据采集 JavaScript Android开发
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
667 7
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
JavaScript 前端开发 API
JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、复杂API请求、DOM操作、搜索和过滤等,array.map()的使用详解(附实际应用代码)
array.map()可以用来数据转换、创建派生数组、应用函数、链式调用、异步数据流处理、复杂API请求梳理、提供DOM操作、用来搜索和过滤等,比for好用太多了,主要是写法简单,并且非常直观,并且能提升代码的可读性,也就提升了Long Term代码的可维护性。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
消息中间件 JavaScript 前端开发
最细最有条理解析:事件循环(消息循环)是什么?为什么JS需要异步
度一教育的袁进老师谈到他的理解:单线程是异步产生的原因,事件循环是异步的实现方式。 本质是因为渲染进程因为计算机图形学的限制,只能是单线程。所以需要“异步”这个技术思想来解决页面阻塞的问题,而“事件循环”是实现“异步”这个技术思想的最主要的技术手段。 但事件循环并不是全部的技术手段,比如Promise,虽然受事件循环管理,但是如果没有事件循环,单一Promise依然能实现异步不是吗? 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您
|
JavaScript 前端开发
Moment.js与其他处理时间戳格式差异的JavaScript库相比有什么优势?
Moment.js与其他处理时间戳格式差异的JavaScript库相比有什么优势?
如何在项目中使用Moment.js库?
如何在项目中使用Moment.js库?

热门文章

最新文章