【问题标题】:Synchronous HTTP request in node.jsnode.js 中的同步 HTTP 请求
【发布时间】: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 不适合你。您无法真正改变面向事件的单线程系统的工作方式。所以你必须改变程序员对代码的看法。

标签: javascript node.js asynchronous


【解决方案1】:

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

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

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

谢谢大家,信守承诺!

【讨论】:

    【解决方案2】:

    如果您觉得必须发出同步 HTTP 请求...

    ...作为Ray Toal pointed outthere's an npm package that does that for you,通过将其卸载到子进程并使用spawnSync同步等待该进程完成。

    它有负面影响,这就是为什么建议不要在 Node 中使用同步 I/O 请求,即使 Node API 提供了一些(fileReadSync 等)。 Node 使用单个 JavaScript 线程。通过在长时间运行的进程(HTTP 请求)上阻塞该线程,可以防止该线程在处理请求时执行任何其他操作。


    如果您只想编写同步的-外观代码...

    ...我建议改用 Promise 和 async/await syntaxasync/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...*/ });
    

    【讨论】:

      猜你喜欢
      • 2018-10-31
      • 2016-05-20
      • 1970-01-01
      • 2015-11-21
      • 2018-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多