node.js

时间:2017-09-06 07:29:56

标签: javascript node.js asynchronous

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

我是Javascript的新手,但我已经阅读了有关Javascript异步的其他答案,以及我应该如何更改我的心理模型并开始使用回调。请不要重复这一点,因为它幼稚而且近视。我的需要是完全合法的,也是一个非常简单的需要。如果我不能按照我想要的方式编写程序,我就会抛弃整个程序并使用不同的语言,唯一的问题是只存在于JS的特定库。

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

编辑:见下文。

2 个答案:

答案 0 :(得分:0)

如果您认为必须发出同步HTTP请求......

...作为Ray Toal pointed outthere'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?

谢谢你们并保持承诺!