如何使用 NodeJS 同步运行两个函数?

Posted

技术标签:

【中文标题】如何使用 NodeJS 同步运行两个函数?【英文标题】:How to run two functions synchronously with NodeJS? 【发布时间】:2018-01-09 17:20:00 【问题描述】:

我对 NodeJS 很陌生,对异步机制迷路了。

我有一个代码应该向第一个 URL 发送 HTTP 发布请求(例如 -https://example.com/first),然后当它得到状态代码 200 的回答时,向同一服务器发送另一个请求,检查服务器是否完成处理第一个请求(例如 - https://example.com/statusCheck)。

服务器应该返回一个文本/纯文本响应,如果它正忙,则包含“true”,如果准备好使用,则包含“false”。 我编写了一个 while 循环,每 2 秒查询一次服务器,最多迭代 10 次。

var request = require('request');

var firstURL = "https://example.com/first";
var serverCheck = "https://example.com/statusCheck";

// Sends up to 10 requests to the server
function checkServerStatus()
    var serverReady = false;
    var count = 0;
    while (!serverReady && count < 10) 
        count++;
        setTimeout(function()
            request.get(serverCheck, function(err, resp, body)
                if (err)
                    console.log(err);
                 else if (body == "false") 
                    generatorReady = true;
                
            )
        , 2000);
    
    return generatorReady;


// Sends the first request and return True if the response equals to 200
function sendFirstRequest()
    var req = request.post(firstURL, function (err, resp, body) 
        if (err) 
            console.log(err);
            return false;
         else if (resp.statusCode === 200)
            return true;
         else 
            return false;
        
    );
;


module.exports = function ()
    // Sends the first request
    var firstRequestStatus = sendFirstRequest();
    if (firstRequestStatus) 
        return checkServerStatus();
    
;

也就是说,我想先运行sendFirstRequest,等待响应,如果响应是true,我想运行checkServerStatus并得到他的返回值。如果可以在每次迭代之间进行睡眠,那就太好了(因为setTimeout 对我也不起作用)。

编辑:我听说我可以使用function* with yieldasync-await 来避免回调地狱——在这种情况下我该如何实现它们?

【问题讨论】:

您可以使用回调来完成。 Example here 嗨,Dan,如果您使用的是节点 8+,请查看 nodejs.org/api/http.html#http_http_request_options_callback --- 关于您的编辑,我认为您不想使用生成器(函数* w/yield ) 和带有 http.request 模块的 async/await 要求您对“承诺”有更好的理解,以便按照您希望它与 async/await 一起使用的方式包装它(并非每个函数都如此) .在这种情况下,我建议从 Promise 开始,并且如您所知,切换到 async/await。 【参考方案1】:

您应该使用 Promise 来执行此操作。下面是一些使用bluebird 的代码,可以满足您的需求。 Promise.any 方法将在 10 次尝试中返回来自 Array 的第一个成功调用。

const Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'));

var firstURL = "https://example.com/";
var serverCheck = "https://example.com/statusCheck";

request.postAsync(firstURL).then(res => 
  if (res.statusCode === 200) return true;
  throw new Error('server not ready');
).then(() =>  
  Promise.any(new Array(10).fill(request.getAsync(serverCheck)))
).then(res => 
  console.log(res);
).catch(err => console.log(err));

【讨论】:

【参考方案2】:

您必须了解异步操作不能在调用后立即返回结果。它们在执行时触发一些处理程序。您可以/应该使用该入口点来启动或继续您​​的逻辑流程。

http.post(params, handler(err, resp, body)
      if(err)
        failFlow(err);
      else if(resp.statusCode === 200) 
        successFlow(resp); 
      
);

您可以根据需要链接任意数量的异步调用,但不能以这种方式返回响应。

您也可能对Promise 的概念感兴趣。

var request = require('request');

var firstURL = "https://example.com/first";
var serverCheck = "https://example.com/statusCheck";
var count = 0;

// Sends up to 10 requests to the server
function checkServerStatus() 
  if (count++ > 10) return;

  request.get(serverCheck, function(err, resp, body) 
    if (err) 
      console.log(err);
      checkServerStatus();
     else if (body == "false") 
      // go further
    
  );


// Sends the first request and return True if the response equals to 200
function sendFirstRequest(cb) 
  var req = request.post(firstURL, function(err, resp, body) 
    if (err) 
      console.log(err);
      return false;
     else if (resp.statusCode === 200) 
      cb();
     else 
      return false;
    
  );
;


module.exports = function() 
  // Sends the first request
  sendFirstRequest(checkServerStatus);
;

【讨论】:

在这种情况下,我可以在// go further 部分返回值或其他内容吗? 在我的真实代码中,函数 sendFirstRequest 正在迭代一个数组并向该数组中存在的任何 URL 发送请求。换句话说,只要服务器不忙,我想要使用不同 URL 发送新请求的代码【参考方案3】:

您可以使用async 库。

您不需要为此设置 setInterval 或任何计时器,只需等待响应即可。

具体来说,您可以为此使用 async.waterfall,例如:

var async = require('async')
var request = require('request')

async.waterfall([
  function(cb) 
    // send the first request
    request.post("https://example.com/first", function (err, resp) 
      // send the response to the next function or break in case there was an error
      cb(err, resp)
    )
  ,
  function(resp, cb) 
    // check for the response
    if (resp.statusCode === 200) 
      // in case the response code is 200 continue to the next function
      return cb()
    

    // if its not 200 break with the response code as an error
    return cb(resp.statusCode)
  ,
  function(cb) 
    // send the verify
    request.get("https://example.com/statusCheck", function (err, resp, body) 
      // send the body of the response to the next function or break in case of an error
      cb(err, body)
    )
  
], function (err, result) 
  // check if there was an error along the way
  if (err) 
    console.log("there was an error", err)
   else 
    // all is good print the result
    console.log("result:", result)
  
)

【讨论】:

【参考方案4】:

async function main() 
  console.log('First call started');
  let response1 = await $.ajax(url: "https://api.stackexchange.com/2.2/questions/269754/answers/?order=desc&site=meta.***&client_id=3519&callback=?")
  console.log('First call finished', response1);
  console.log('Second call started');
  let response2 = await $.ajax(url: "https://api.stackexchange.com/2.2/questions/269754/answers/?order=desc&site=meta.***&client_id=3519&callback=?")
  console.log('Second call finished',response2);


main();
&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;

在较新版本的 nodejs 中,您可以像上面的示例一样使用 async await

请注意 $.ajax 不是节点函数。只是为了演示

您可以在任何返回 Promise 的函数上使用 await。 对于下一个示例,您需要安装请求包并使用 Node >= 8 来使用 promisify

const promisify = require('util');
const request = require('request')

async function main() 
    let get = promisify(request);
    let response1 = await get('https://www.random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain&rnd=new');
    console.log('first random: ',response1.body)
    let response2 = await get('https://www.random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain&rnd=new');
    console.log('second random: ',response2.body)


main();

http://2ality.com/2017/05/util-promisify.html

https://github.com/request/request

【讨论】:

我是否会使用另一个 AJAX 生成器而不是 jQuery? 没关系。任何返回承诺的东西都可以。我在答案中添加了一个示例

以上是关于如何使用 NodeJS 同步运行两个函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何优雅的处理Nodejs中的异步回调

如何优雅的处理Nodejs中的异步回调

如何在nodejs中结合同步和异步功能

如何使用两个不同的节点版本运行两个不同的nodejs应用程序[关闭]

NodeJS实现同步的方法

如何同步节点中的两个异步调用?