如果您觉得必须发出同步 HTTP 请求...
...作为Ray Toal pointed out、there's an npm package that does that for you,通过将其卸载到子进程并使用spawnSync同步等待该进程完成。
它有负面影响,这就是为什么建议不要在 Node 中使用同步 I/O 请求,即使 Node API 提供了一些(fileReadSync 等)。 Node 使用单个 JavaScript 线程。通过在长时间运行的进程(HTTP 请求)上阻塞该线程,可以防止该线程在处理请求时执行任何其他操作。
如果您只想编写同步的-外观代码...
...我建议改用 Promise 和 async/await syntax。 async/await 语法为看起来同步的代码带来了异步处理,提供了使用类似同步语法的异步处理的好处。最新版本的 Node 使用最新版本的 V8 JavaScript 引擎,支持async/await。
Node 早于 Promise,并使用自己的 API 约定进行回调,但有一些包(例如 promisify)可以将 Node-callback 风格的 API 转换为基于 Promise 的 API。 promisify 在 API 级别工作,因此只需几行代码,您就可以转换整个 fs 模块的 API。随着时间的推移,我们可以期待 Promise 会被内置到新的包中(我怀疑也会被改造成标准的 Node API)。
对于http.request,它比仅仅更新 API 要复杂一些,因为需要响应一些事件。但“多一点”就是它的全部。这是一个快速简单的版本,它还添加了Content-Length 标头的自动处理:
const requestPromise = (options, postData = null) => new Promise((resolve, reject) => {
const isPostWithData = options && options.method === "POST" && postData !== null;
if (isPostWithData && (!options.headers || !options.headers["Content-Length"])) {
// Convenience: Add Content-Length header
options = Object.assign({}, options, {
headers: Object.assign({}, options.headers, {
"Content-Length": Buffer.byteLength(postData)
})
});
}
const body = [];
const req = http.request(options, res => {
res.on('data', chunk => {
body.push(chunk);
});
res.on('end', () => {
res.body = Buffer.concat(body);
resolve(res);
});
});
req.on('error', e => {
reject(e);
});
if (isPostWithData) {
req.write(postData);
}
req.end();
});
在我们的工具包中,我们可以像这样在async 函数中发出异步请求:
try {
const res = await requestPromise(/*...options...*/, /*...data if needed...*/);
console.log(res.body.toString("utf8"));
// ...continue with logic...
} catch (e) {
console.error(e);
}
如您所见,代码的逻辑流程与同步代码一样。但代码不是同步的。 JavaScript 线程运行代码直到并包括作为第一个await 的操作数的表达式,然后在异步进程运行时执行其他操作;稍后,当异步过程完成时,它会从中断处继续,将结果分配给res,然后执行console.log,等等——直到下一个await(如果有)。如果 promise await 消费的结果是拒绝,则将其作为异常处理并传递给 try/catch 中的 catch。
这是the example from http.request,使用我们上面的requestPromise:
try {
const res = await requestPromise(
{
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
},
querystring.stringify({
'msg': 'Hello World!'
}
);
console.log(res.body.toString("utf8"));
} catch (e) {
console.error(e);
}
要使用await,您必须在async 函数中。您可能只是将整个模块代码包装在一个中:
(async () => {
// ...module code here...
})();
如果您的代码有任何可能无法捕获错误,请在末尾添加一个包罗万象的catch 处理程序:
(async () => {
// ...module code here...
})().catch(e => { /* ...handle the error here...*/ });