我使用node.js 8.4.0并且我想编写一个执行HTTP请求并从服务器返回结果的函数。这意味着该函数应该等到请求完成。
我是Javascript的新手,但我已经阅读了有关Javascript异步的其他答案,以及我应该如何更改我的心理模型并开始使用回调。请不要重复这一点,因为它幼稚而且近视。我的需要是完全合法的,也是一个非常简单的需要。如果我不能按照我想要的方式编写程序,我就会抛弃整个程序并使用不同的语言,唯一的问题是只存在于JS的特定库。
尽可能地,我正在寻找一种简单,通用,强大且可移植的方式来等待回调。理想情况下,不仅适用于http,还适用于任何其他异步内容。我知道异步编程是一件好事,但只有当你真的需要它时。我没有。
编辑:见下文。
答案 0 :(得分:0)
如果您认为必须发出同步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请求)上阻塞该线程,可以防止该线程在处理请求时执行任何其他操作。
如果您只想编写同步 - 正在寻找 代码......
......我建议使用promises和async
/await
syntax代替。 async
/ await
语法为看起来同步的代码带来了异步处理,提供了使用类似同步语法的异步处理的好处。最新版本的Node使用最新版本的V8 JavaScript引擎,该引擎支持async
/ await
。
节点优先于Promises,并使用自己的API约定进行回调,但是有promisify这样的包可以将节点回调样式API转换为基于Promise的API。 promisify
适用于API级别,因此您可以使用几行代码转换整个fs
模块的API。随着时间的推移,我们可以预期承诺会内置到新的软件包中(我怀疑也会改进标准的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
(如果有的话) )。如果承诺await
消费的结果是拒绝,则会将其作为例外处理并传递到catch
/ try
中的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...*/ });
答案 1 :(得分:0)
作为参考资料以及将来遇到此问题的任何人,这是我正在寻找的解释:
在Javascript中没有同步请求/ join / waitFor这样的东西,因为所有内容都在同一个线程上运行,甚至回调。回调被添加到事件队列中,并在线程从外部作用域返回后进行处理。如果可能的话,等待任何事情都会导致僵局。
此视频很好地解释了这一点:Philip Roberts: What the heck is the event loop anyway?
谢谢你们并保持承诺!