node.js 中的同步 HTTP 请求

Posted

技术标签:

【中文标题】node.js 中的同步 HTTP 请求【英文标题】:Synchronous HTTP request in node.js 【发布时间】:2018-02-14 14:15:56 【问题描述】:

我正在使用 node.js 8.4.0,我想编写一个执行 HTTP 请求并从服务器返回结果的函数。这意味着函数应该等到请求完成。

我是 javascript 新手,但我已经阅读了其他关于 Javascript 是异步的以及我应该如何改变我的心智模型并开始使用回调的答案。请不要费心重申这一点,因为它幼稚且短视。我的需求是完全合法的,也是一个非常简单的需求。如果我无法按照我想要的方式编写程序,我将放弃整个程序并使用不同的语言,唯一的问题是仅针对 JS 存在的特定库。

我尽可能地寻找一种简单、通用、健壮且可移植的方式来等待回调。理想情况下,不仅适用于 http,还适用于任何其他异步内容。我知道异步编程是一件很棒的事情,但只有在你真正需要它的时候。我没有。

编辑:见下文。

【问题讨论】:

没有。不不不。不。 How to hack a single-threaded asynchronous engine and force it to perform synchronous stuff = 给我的大红色警报。相反,拥抱异步的力量并学习如何处理它。或者回到 php I'm looking for a way to wait for a callback --> 你不需要等待回调。回调是一个函数,只要您要求的事情完成,就会触发。同时,脚本执行不会被阻塞。这就是异步的原理。 如果您使用 google 同步 HTTP 请求,这是您首先得到的:npmjs.com/package/sync-request “请不要费心重复这句话,因为这既幼稚又短视。”嗯,这是激励人们帮助你的好方法。 放弃整个事情并使用另一种语言。我是认真的。 Node.js 和 javascript 是我的首选工具,但你必须改变你的思维模式并学习异步编程。如果你拒绝这样做,那么 js 不适合你。您无法真正改变面向事件的单线程系统的工作方式。所以你必须改变程序员对代码的看法。 【参考方案1】:

作为参考和将来遇到此问题的任何人,这是我正在寻找的解释:

在 Javascript 中没有同步请求/加入/waitFor 这样的东西,因为一切都在同一个线程上运行,甚至回调。回调被添加到事件队列并在线程从外部范围返回后处理。等待任何事情,如果可能的话,基本上会导致死锁。

这个视频很好地解释了它:Philip Roberts: What the heck is the event loop anyway?

谢谢大家,信守承诺!

【讨论】:

【参考方案2】:

如果您觉得必须发出同步 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...*/ );

【讨论】:

以上是关于node.js 中的同步 HTTP 请求的主要内容,如果未能解决你的问题,请参考以下文章

node.js 中的 Http 请求重定向和 cookie 处理

在 Node.js 中的单个 HTTP 请求中调用多个 HTTP 请求

想要在 node.js 中同步发送请求

使Node.js中的http.request适用于浏览器

Node.JS 中的 HTTP DELETE 动词

深入node.js 6 node中的Http服务器