如何使用yield同时发起多个HTTP请求

现在的业务里有一个场景是需要向一个HTTP协议的接口按ID请求大量的数据,由于采用的开发语言是Node.js,因此非常希望可以利用Node.js提供的异步IO的能力来提高获取多个不同ID数据时的效率。因为现在写的是ES6规范的代码,因此不想再像一开始写node代码那样子继续使用async然后陷入回调地狱了,恰好这几天看到了一篇文章,说是用yield同样可以实现这种效果。试验了一下果然可以,特意写篇文章水一下经验。

搭建一个延迟返回的服务

首先必须有一个响应很慢,耗时很稳定的接口才能做这类型的试验,为此我自己用Ruby加上Sinatra搭建了一个HTTP服务,接口不干什么事,纯粹就是睡眠无秒钟然后返回,代码很简单,如下

require 'sinatra'

get '/' do
  sleep 5
  'OK'
end

假设我把上面的代码保存到了一个叫做a.rb的文件中——不用假设了,我真的是这么干的,那么,使用下面的命令启动这样的一个HTTP服务,监听4567端口

ruby a.rb

串行的HTTP请求

在给出并发地发起HTTP请求的代码之前,先来一段串行地发起HTTP请求的代码。此处选用的HTTP客户端是co-request,为了可以使用关键字yield,还引入了co这个库,具体代码如下

'use strict';

const co = require('co');
const request = require('co-request');

co(function* () {
  yield request('http://localhost:4567');
  yield request('http://localhost:4567');
  console.log('OK');
});

把上述代码保存为文件test_yield_sync.js中,使用node命令执行该文件,同时使用time命令对这个过程进行计时。在我的机器上的某一次运行耗时11.992秒,与两次单独接口请求的耗时相当,显然这样的代码在发起HTTP请求时是串行的。

并发的HTTP请求

据某篇已经不记得标题和地址的文章所言,只要把要发起的请求都做成Promise,然后把多个Promise放到一个数组中,并用yield作用在这个数组上,就可以实现并发地发起请求的功能了。按照这个思路对上面的代码稍加改造,得到如下结果

'use strict';

const co = require('co');
const request = require('co-request');

co(function* () {
  yield [
    request('http://localhost:4567'),
    request('http://localhost:4567'),
  ];
  console.log('OK');
});

再次使用time计时,得到的结果是5.320秒,接近于单独请求一次接口的耗时,而从服务输出的内容可以确认到这实际上是完成了两次HTTP请求的耗时——恭喜恭喜,这样子确实是可以实现并发地请求多个HTTP接口的。不过实际上我也是个Node.js小白,具体的原理我也不清楚,大家还是自己搜罗文章看看为什么可以这样做吧,全文完。

扩展阅读

ES6 generator函数与co再一瞥