如果我需要按顺序调用3个http API,那么对于以下代码来说,这将是一个更好的替代方法:
http.get({ host: 'www.example.com', path: '/api_1.php' }, function(res) {
res.on('data', function(d) {
http.get({ host: 'www.example.com', path: '/api_2.php' }, function(res) {
res.on('data', function(d) {
http.get({ host: 'www.example.com', path: '/api_3.php' }, function(res) {
res.on('data', function(d) {
});
});
}
});
});
}
});
});
}
答案 0 :(得分:68)
使用Futures
之类的延迟。
var sequence = Futures.sequence();
sequence
.then(function(next) {
http.get({}, next);
})
.then(function(next, res) {
res.on("data", next);
})
.then(function(next, d) {
http.get({}, next);
})
.then(function(next, res) {
...
})
如果您需要传递范围,那么只需执行此类操作
.then(function(next, d) {
http.get({}, function(res) {
next(res, d);
});
})
.then(function(next, res, d) { })
...
})
答案 1 :(得分:53)
我也喜欢Raynos的解决方案,但我更喜欢不同的流量控制库。
https://github.com/caolan/async
根据您是否需要每个后续功能的结果,我要么使用系列,并行或瀑布。
Series当必须连续执行时,但在每个后续函数调用中不一定需要结果。
Parallel如果它们可以并行执行,则在每个并行函数期间不需要每个函数的结果,并且在完成所有函数时都需要回调。
Waterfall如果你想在每个函数中变换结果并传递给下一个
endpoints =
[{ host: 'www.example.com', path: '/api_1.php' },
{ host: 'www.example.com', path: '/api_2.php' },
{ host: 'www.example.com', path: '/api_3.php' }];
async.mapSeries(endpoints, http.get, function(results){
// Array of results
});
答案 2 :(得分:33)
您可以使用我的Common Node library:
执行此操作function get(url) {
return new (require('httpclient').HttpClient)({
method: 'GET',
url: url
}).finish().body.read().decodeToString();
}
var a = get('www.example.com/api_1.php'),
b = get('www.example.com/api_2.php'),
c = get('www.example.com/api_3.php');
答案 3 :(得分:28)
到目前为止,我发现和使用的最简单的是sync-request,它支持节点和浏览器!
var request = require('sync-request');
var res = request('GET', 'http://google.com');
console.log(res.body.toString('utf-8'));
这就是它,没有疯狂的配置,没有复杂的lib安装,尽管它确实有一个lib回退。只是工作。我在这里尝试了其他的例子,当有很多额外的设置要做或安装没有工作时,我感到难过!
使用res.getBody()
时sync-request使用的示例并不好用,所有get body都接受编码并转换响应数据。只需改为res.body.toString(encoding)
。
答案 4 :(得分:20)
我使用带有apis列表的递归函数
var APIs = [ '/api_1.php', '/api_2.php', '/api_3.php' ];
var host = 'www.example.com';
function callAPIs ( host, APIs ) {
var API = APIs.shift();
http.get({ host: host, path: API }, function(res) {
var body = '';
res.on('data', function (d) {
body += d;
});
res.on('end', function () {
if( APIs.length ) {
callAPIs ( host, APIs );
}
});
});
}
callAPIs( host, APIs );
编辑:请求版本
var request = require('request');
var APIs = [ '/api_1.php', '/api_2.php', '/api_3.php' ];
var host = 'www.example.com';
var APIs = APIs.map(function (api) {
return 'http://' + host + api;
});
function callAPIs ( host, APIs ) {
var API = APIs.shift();
request(API, function(err, res, body) {
if( APIs.length ) {
callAPIs ( host, APIs );
}
});
}
callAPIs( host, APIs );
编辑:请求/异步版
var request = require('request');
var async = require('async');
var APIs = [ '/api_1.php', '/api_2.php', '/api_3.php' ];
var host = 'www.example.com';
var APIs = APIs.map(function (api) {
return 'http://' + host + api;
});
async.eachSeries(function (API, cb) {
request(API, function (err, res, body) {
cb(err);
});
}, function (err) {
//called when all done, or error occurs
});
答案 5 :(得分:5)
似乎这个问题的解决方案永无止境,这里还有一个:)
// do it once.
sync(fs, 'readFile')
// now use it anywhere in both sync or async ways.
var data = fs.readFile(__filename, 'utf8')
答案 6 :(得分:5)
另一种可能性是建立一个跟踪已完成任务的回调:
function onApiResults(requestId, response, results) {
requestsCompleted |= requestId;
switch(requestId) {
case REQUEST_API1:
...
[Call API2]
break;
case REQUEST_API2:
...
[Call API3]
break;
case REQUEST_API3:
...
break;
}
if(requestId == requestsNeeded)
response.end();
}
然后只需为每个ID分配一个ID,您就可以在关闭连接之前设置必须完成哪些任务的要求。
const var REQUEST_API1 = 0x01;
const var REQUEST_API2 = 0x02;
const var REQUEST_API3 = 0x03;
const var requestsNeeded = REQUEST_API1 | REQUEST_API2 | REQUEST_API3;
好吧,它不漂亮。这只是进行顺序调用的另一种方式。很遗憾NodeJS不提供最基本的同步调用。但我理解异步性的诱惑是什么。
答案 7 :(得分:4)
使用sequenty。
sudo npm install sequenty
或
https://github.com/AndyShin/sequenty
很简单。var sequenty = require('sequenty');
function f1(cb) // cb: callback by sequenty
{
console.log("I'm f1");
cb(); // please call this after finshed
}
function f2(cb)
{
console.log("I'm f2");
cb();
}
sequenty.run([f1, f2]);
你也可以使用这样的循环:
var f = [];
var queries = [ "select .. blah blah", "update blah blah", ...];
for (var i = 0; i < queries.length; i++)
{
f[i] = function(cb, funcIndex) // sequenty gives you cb and funcIndex
{
db.query(queries[funcIndex], function(err, info)
{
cb(); // must be called
});
}
}
sequenty.run(f); // fire!
答案 8 :(得分:3)
使用request库可以帮助减少错误:
var request = require('request')
request({ uri: 'http://api.com/1' }, function(err, response, body){
// use body
request({ uri: 'http://api.com/2' }, function(err, response, body){
// use body
request({ uri: 'http://api.com/3' }, function(err, response, body){
// use body
})
})
})
但是为了获得最大的惊人效果,您应该尝试一些像Step这样的控制流库 - 它还允许您并行化请求,假设它是可接受的:
var request = require('request')
var Step = require('step')
// request returns body as 3rd argument
// we have to move it so it works with Step :(
request.getBody = function(o, cb){
request(o, function(err, resp, body){
cb(err, body)
})
}
Step(
function getData(){
request.getBody({ uri: 'http://api.com/?method=1' }, this.parallel())
request.getBody({ uri: 'http://api.com/?method=2' }, this.parallel())
request.getBody({ uri: 'http://api.com/?method=3' }, this.parallel())
},
function doStuff(err, r1, r2, r3){
console.log(r1,r2,r3)
}
)
答案 9 :(得分:2)
有很多控制流库 - 我喜欢conseq(...因为我写了它。)另外,on('data')
可以多次触发,所以使用像{{{{}}这样的REST包装器库3}}
Seq()
.seq(function () {
rest.get('http://www.example.com/api_1.php').on('complete', this.next);
})
.seq(function (d1) {
this.d1 = d1;
rest.get('http://www.example.com/api_2.php').on('complete', this.next);
})
.seq(function (d2) {
this.d2 = d2;
rest.get('http://www.example.com/api_3.php').on('complete', this.next);
})
.seq(function (d3) {
// use this.d1, this.d2, d3
})
答案 10 :(得分:2)
从2018年开始,使用ES6模块和Promises,我们可以编写这样的函数:
writer
,然后在另一个模块中
class Writer < ApplicationRecord
has_many :notes
has_many :notebooks, through: :notes
belongs_to :current_notebook, class: 'Notebook'
end
class Note < ApplicationRecord
# ...
validate :is_current_notebook
def is_current_notebook
unless notebook == writer.current_notebook
errors.add(:notebook, 'is not valid.')
end
end
end
代码需要在异步上下文中执行(使用import { get } from 'http';
export const fetch = (url) => new Promise((resolve, reject) => {
get(url, (res) => {
let data = '';
res.on('end', () => resolve(data));
res.on('data', (buf) => data += buf.toString());
})
.on('error', e => reject(e));
});
关键字)
答案 11 :(得分:2)
Raynos已经很好地回答了这个问题。然而,自答案发布以来,序列库已发生变化。
要使序列正常工作,请点击此链接:https://github.com/FuturesJS/sequence/tree/9daf0000289954b85c0925119821752fbfb3521e。
这是{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}
:
npm install sequence
答案 12 :(得分:1)
这里是@andy-shin的版本,后面是数组中的参数而不是索引:
function run(funcs, args) {
var i = 0;
var recursive = function() {
funcs[i](function() {
i++;
if (i < funcs.length)
recursive();
}, args[i]);
};
recursive();
}
答案 13 :(得分:1)
我登陆这里因为我需要对http.request进行速率限制(~10k聚合查询到弹性搜索以构建分析报告)。以下只是窒息了我的机器。
for (item in set) {
http.request(... + item + ...);
}
我的网址非常简单,所以这可能不会轻易应用于原始问题,但我认为它既可能适用,也值得在这里为读者写一些与我类似的问题,并且想要一个简单的JavaScript无库解决方案。
我的工作不依赖于订单,我的第一种方法是将它包装在一个shell脚本中以便将其包装起来(因为我是JavaScript的新手)。这是功能但不令人满意。我最终的JavaScript解决方案是执行以下操作:
var stack=[];
stack.push('BOTTOM');
function get_top() {
var top = stack.pop();
if (top != 'BOTTOM')
collect(top);
}
function collect(item) {
http.request( ... + item + ...
result.on('end', function() {
...
get_top();
});
);
}
for (item in set) {
stack.push(item);
}
get_top();
看起来像收集和 get_top 之间的相互递归。我不确定它是否有效,因为系统是异步的,而 collect 函数在 on上的事件中被隐藏了一个回调。('end'。
我认为适用于原始问题是一般的。如果像我的场景一样知道序列/集合,则可以在一个步骤中将所有URL /键推送到堆栈中。如果按照你的方式计算它们, on('end'函数可以在 get_top()之前推送堆栈上的下一个url。如果有的话,结果会少一些嵌套,当您调用的API发生更改时,可能更容易重构。
我意识到这实际上相当于@ generalhenry上面简单的递归版本(所以我赞成了!)
答案 14 :(得分:1)
...... 4年后......
以下是框架Danf的原始解决方案(您不需要任何代码来处理此类事情,只需要一些配置):
// config/common/config/sequences.js
'use strict';
module.exports = {
executeMySyncQueries: {
operations: [
{
order: 0,
service: 'danf:http.router',
method: 'follow',
arguments: [
'www.example.com/api_1.php',
'GET'
],
scope: 'response1'
},
{
order: 1,
service: 'danf:http.router',
method: 'follow',
arguments: [
'www.example.com/api_2.php',
'GET'
],
scope: 'response2'
},
{
order: 2,
service: 'danf:http.router',
method: 'follow',
arguments: [
'www.example.com/api_3.php',
'GET'
],
scope: 'response3'
}
]
}
};
对要并行执行的操作使用相同的
order
值。
如果你想更短,你可以使用收集过程:
// config/common/config/sequences.js
'use strict';
module.exports = {
executeMySyncQueries: {
operations: [
{
service: 'danf:http.router',
method: 'follow',
// Process the operation on each item
// of the following collection.
collection: {
// Define the input collection.
input: [
'www.example.com/api_1.php',
'www.example.com/api_2.php',
'www.example.com/api_3.php'
],
// Define the async method used.
// You can specify any collection method
// of the async lib.
// '--' is a shorcut for 'forEachOfSeries'
// which is an execution in series.
method: '--'
},
arguments: [
// Resolve reference '@@.@@' in the context
// of the input item.
'@@.@@',
'GET'
],
// Set the responses in the property 'responses'
// of the stream.
scope: 'responses'
}
]
}
};
请查看框架的overview以获取更多信息。
答案 15 :(得分:0)
此代码可用于依次和顺序执行诺言数组,之后您可以在.then()
调用中执行最终代码。
const allTasks = [() => promise1, () => promise2, () => promise3];
function executePromisesSync(tasks) {
return tasks.reduce((task, nextTask) => task.then(nextTask), Promise.resolve());
}
executePromisesSync(allTasks).then(
result => console.log(result),
error => console.error(error)
);
答案 16 :(得分:0)
这是另一个基于请求并使用promises的同步模块。超级简单易用,适用于摩卡测试。
if (!mysqli_query($con, "LOAD DATA LOCAL INFILE '/home/user/movies.txt' INTO TABLE films")) {
printf("Errormessage: %s\n", mysqli_error($con));
}
$result = mysqli_query($con, "SELECT title, yr, genre, plot, rating FROM films") or die("Unable to query.");
npm install super-request
答案 17 :(得分:0)
我实际上得到了您(和我)想要的东西,而没有使用wait,Promises或任何(外部)库(我们自己的库除外)的包含。
方法如下:
我们将使一个C ++模块与node.js一起使用,并且该C ++模块函数将发出HTTP请求并以字符串形式返回数据,您可以通过以下方式直接使用它:
var myData = newModule.get(url);
您准备好开始了吗?
步骤1: 在计算机上的其他位置创建一个新文件夹,我们仅使用该文件夹来构建module.node文件(从C ++编译),以后可以将其移动。
在新文件夹中(我将其放在mynewFolder / src中以进行组织):
npm init
然后
npm install node-gyp -g
现在制作2个新文件: 1,调用something.cpp并将其放入其中(或根据需要对其进行修改):
#pragma comment(lib, "urlmon.lib")
#include <sstream>
#include <WTypes.h>
#include <node.h>
#include <urlmon.h>
#include <iostream>
using namespace std;
using namespace v8;
Local<Value> S(const char* inp, Isolate* is) {
return String::NewFromUtf8(
is,
inp,
NewStringType::kNormal
).ToLocalChecked();
}
Local<Value> N(double inp, Isolate* is) {
return Number::New(
is,
inp
);
}
const char* stdStr(Local<Value> str, Isolate* is) {
String::Utf8Value val(is, str);
return *val;
}
double num(Local<Value> inp) {
return inp.As<Number>()->Value();
}
Local<Value> str(Local<Value> inp) {
return inp.As<String>();
}
Local<Value> get(const char* url, Isolate* is) {
IStream* stream;
HRESULT res = URLOpenBlockingStream(0, url, &stream, 0, 0);
char buffer[100];
unsigned long bytesReadSoFar;
stringstream ss;
stream->Read(buffer, 100, &bytesReadSoFar);
while(bytesReadSoFar > 0U) {
ss.write(buffer, (long long) bytesReadSoFar);
stream->Read(buffer, 100, &bytesReadSoFar);
}
stream->Release();
const string tmp = ss.str();
const char* cstr = tmp.c_str();
return S(cstr, is);
}
void Hello(const FunctionCallbackInfo<Value>& arguments) {
cout << "Yo there!!" << endl;
Isolate* is = arguments.GetIsolate();
Local<Context> ctx = is->GetCurrentContext();
const char* url = stdStr(arguments[0], is);
Local<Value> pg = get(url,is);
Local<Object> obj = Object::New(is);
obj->Set(ctx,
S("result",is),
pg
);
arguments.GetReturnValue().Set(
obj
);
}
void Init(Local<Object> exports) {
NODE_SET_METHOD(exports, "get", Hello);
}
NODE_MODULE(cobypp, Init);
现在在一个名为something.gyp
的目录中创建一个新文件,并将其放入(类似)它:
{
"targets": [
{
"target_name": "cobypp",
"sources": [ "src/cobypp.cpp" ]
}
]
}
现在在package.json文件中,添加:"gypfile": true,
现在:在控制台中,node-gyp rebuild
如果遍历整个命令并且最后没有错误地说“ ok”,那么您(几乎)很高兴,如果没有,请发表评论。
但是,如果可行,则转到build / Release / cobypp.node(或任何您需要的文件),将其复制到您的主node.js文件夹中,然后复制到node.js中:
var myCPP = require("./cobypp")
var myData = myCPP.get("http://google.com").result;
console.log(myData);
..
response.end(myData);//or whatever