Node.js 中的同步请求

如何使 Node.js 中的“ request”模块以同步方式加载内容?我所见过的最好的建议就是以某种方式使用回调函数,让函数在完成之前不返回自己。我尝试在代码中内联使用“ request”函数(需要根据不能放在回调中的数据来处理事情)。

那么,我如何使用“ request”模块的回调来保证它在加载完资源之前不会返回自己呢?

我所做的是运行一个循环,从 API 下载两个值,然后必须根据这些值进行一些计算。虽然数学可以在回调中完成... ... 但是循环将在没有执行下一个操作所需的值的情况下进行。(因此,在数据准备就绪之前停止循环将解决这个问题)

    /* loop */ {
/* URL Generation */




request( {url: base + u_ext}, function( err, res, body ) {
var split1 = body.split("\n");
var split2 = split1[1].split(", ");
ucomp = split2[1];
});


request( {url: base + v_ext}, function( err, res, body ) {
var split1 = body.split("\n");
var split2 = split1[1].split(", ");
vcomp = split2[1];
});


/* math which needs to be after functions get variables and before loop advances */
}
150094 次浏览

简短的回答是: 不要。如果想要线性读取的代码,可以使用类似 seq. 的库。但别指望同步。真的不行。这是件好事。

几乎没有什么是复试不能做的。如果它们依赖于公共变量,则创建一个包含它们的闭包。手头的实际任务是什么?

您需要一个计数器,并且只有在数据存在时才调用回调:

var waiting = 2;
request( {url: base + u_ext}, function( err, res, body ) {
var split1 = body.split("\n");
var split2 = split1[1].split(", ");
ucomp = split2[1];
if(--waiting == 0) callback();
});


request( {url: base + v_ext}, function( err, res, body ) {
var split1 = body.split("\n");
var split2 = split1[1].split(", ");
vcomp = split2[1];
if(--waiting == 0) callback();
});


function callback() {
// do math here.
}

更新2018: node.js 在最近的版本中支持异步/等待关键字,并且使用代表异步进程的库作为承诺,您可以等待它们。您可以在程序中获得线性的、顺序的流程,并且在您等待的过程中其他工作可以继续进行。建得很好,值得一试。

您应该看一下称为 异步的库

and try to use async.series call for your problem.

阿瑞德里德尔的回答相对比较好(否决了它) ,但我认为它缺乏循环等价物。这应该能帮助你:

Sync code equivalent:

while (condition) {
var data = request(url);
<math here>
}
return result;

串行执行的异步代码:

function continueLoop() {
if (!condition) return cb(result);
request(url, function(err, res, body) {
<math here>
continueLoop()
})
}
continueLoop()

虽然异步样式可能是 node.js 的本质,通常您不应该这样做,但是有时您希望这样做。

I'm writing a handy script to check an API and want not to mess it up with callbacks.

Javascript 不能执行同步请求,但是 C 库可以。

Https://github.com/dhruvbird/http-sync

The short answer is: don't. (...) You really can't. And that's a good thing

我想澄清一下:

支持 同步请求。它的设计并不是为了支持他们开箱即用,但是如果你足够热心的话,有一些变通方法,这里有一个例子:

var request = require('sync-request'),
res1, res2, ucomp, vcomp;


try {
res1 = request('GET', base + u_ext);
res2 = request('GET', base + v_ext);
ucomp = res1.split('\n')[1].split(', ')[1];
vcomp = res2.split('\n')[1].split(', ')[1];
doSomething(ucomp, vcomp);


} catch (e) {}

当你打开“ sync-request”库的引擎盖时,你可以看到它在后台运行一个同步的 儿童程序。正如同步请求 README中所解释的那样,应该明智地使用 非常。这种方法锁定主线程,这对性能不利。

但是,在某些情况下,编写异步解决方案几乎没有什么好处(相比之下,编写难以阅读的代码会造成一定的损害)。

这是许多其他语言(Python、 Java、 C # 等)中的 HTTP 请求库所持有的默认假设,这种理念也可以应用到 JavaScript 中。语言毕竟是解决问题的工具,如果利大于弊,有时您可能不会使用 想要回调。

For JavaScript purists this may rankle of heresy, but I'm a pragmatist so I can clearly see that the simplicity of using synchronous requests helps if you find yourself in some of the following scenarios:

  1. 测试自动化 (测试通常是同步的)。

  2. QuickAPI mashup (即编程马拉松、概念工程验证等)。

  3. 帮助初学者的简单例子 (之前和之后)。

请注意,上面的代码应该使用 没有进行生产。如果你要运行一个合适的 API,那么使用回调、承诺、使用异步/等待或其他方式,但是避免使用同步代码,除非你想在服务器上浪费大量的 CPU 时间。

在2018年,您可以在 Node.js 中使用 asyncawait编写“通常”样式。

下面是一个示例,它将请求回调包装在一个承诺中,然后使用 await获取已解析的值。

const request = require('request');


// wrap a request in an promise
function downloadPage(url) {
return new Promise((resolve, reject) => {
request(url, (error, response, body) => {
if (error) reject(error);
if (response.statusCode != 200) {
reject('Invalid status code <' + response.statusCode + '>');
}
resolve(body);
});
});
}


// now to program the "usual" way
// all you need to do is use async functions and await
// for functions returning promises
async function myBackEndLogic() {
try {
const html = await downloadPage('https://microsoft.com')
console.log('SHOULD WORK:');
console.log(html);


// try downloading an invalid url
await downloadPage('http://      .com')
} catch (error) {
console.error('ERROR:');
console.error(error);
}
}


// run your async function
myBackEndLogic();

您可以对请求库进行类似的操作,但是这是使用 const https = require('https');const http = require('http');进行同步,它们应该与节点一起提供。

举个例子,

const https = require('https');


const http_get1 = {
host : 'www.googleapis.com',
port : '443',
path : '/youtube/v3/search?arg=1',
method : 'GET',
headers : {
'Content-Type' : 'application/json'
}
};


const http_get2 = {
host : 'www.googleapis.com',
port : '443',
path : '/youtube/v3/search?arg=2',
method : 'GET',
headers : {
'Content-Type' : 'application/json'
}
};


let data1 = '';
let data2 = '';


function master() {


if(!data1)
return;


if(!data2)
return;


console.log(data1);
console.log(data2);


}


const req1 = https.request(http_get1, (res) => {
console.log(res.headers);


res.on('data', (chunk) => {
data1 += chunk;
});


res.on('end', () => {
console.log('done');
master();
});
});




const req2 = https.request(http_get2, (res) => {
console.log(res.headers);


res.on('data', (chunk) => {
data2 += chunk;
});


res.on('end', () => {
console.log('done');
master();
});
});


req1.end();
req2.end();

可以使用 retus进行跨平台同步 HTTP 请求。这是一个基于 sync-request的库,但是我添加了一些舒适的特性:

const retus = require("retus");


const { body } = retus("https://google.com");
//=> "<!doctype html>..."

That's it!

参见 同步请求: https://github.com/ForbesLindesay/sync-request

Example:

var request = require('sync-request');
var res = request('GET', 'http://example.com');
console.log(res.getBody());

我自己想到的最简单的解决方案是使用节点的本机“ child _ request”,并在其中使用一个简单的 curl 命令调用 exec。所有的依赖关系和异步性对于我来说太麻烦了,因为我只是想“把 http 响应保存到节点中的一个变量”

如今,如果你想按顺序做事,你可以这样做:

for (const task of tasks) {
const response = await request(...);
}

这段代码(上面)按顺序运行请求,但不是同步的(这意味着它不会阻塞主线程)。如果您真的需要同步,那么这些库中的大多数都可以归结为:

const child = require('child_process');
const buffer = child.execSync(`curl https://example.com/foo.json`);
const data = JSON.parse(buffer.toString('utf8'));

写这篇文章的时候,所有的答案都是:

  1. 但使用控制流进行同步(例如使用异步库)
  2. 同步,但是阻塞(这意味着 Node 上的所有其他线程都停止了,从性能上来说这是很糟糕的) ,例如 retus 或 Sync-request。
  3. Out of date, such as http-request.

检查非阻塞 同步请求,基于 异步

Or see some of the answers in this thread, such as 这个, which use deasync directly.