简体   繁体   English

如何与NodeJS同步运行两个功能?

[英]How to run two functions synchronously with NodeJS?

I am pretty new with NodeJS and got lost with the asynchronous mechanism. 我对NodeJS相当陌生,并且对异步机制一无所知。

I have a code that should send a HTTP post request to the first URL (for example - https://example.com/first ), and then when it got answered with status code 200, send another request to the same server that checks if the server is done with processing the first request (for example - https://example.com/statusCheck ). 我有一个代码,应该将HTTP发布请求发送到第一个URL(例如-https://example.com/first ),然后在收到状态代码200答复时,将另一个请求发送到检查该服务器的同一服务器如果服务器已处理完第一个请求(例如-https://example.com/statusCheck )。

The server should return a text/plain response contains "true" if it's busy, and "false" if it's ready to use. 服务器应该返回一个文本/纯文本响应,如果繁忙则返回“ true”,如果准备就绪则返回“ false”。 I wrote it with a while loop that queries the server every 2 seconds, up to maximum of 10 iterates. 我编写了一个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();
    }
};

In other words, I want to run sendFirstRequest first, wait for the response, and in case that the response is true , I want to run the checkServerStatus and get his returned value. 换句话说,我想先运行sendFirstRequest ,等待响应,如果响应为true ,我想运行checkServerStatus并获取他的返回值。 If it's possible to do it with a sleep between each while iterate, it will be great (because the setTimeout does not work for me as well) . 如果有可能在每次迭代之间进行一次睡眠,那将是很棒的(因为setTimeout也不适用于我)。

Edit: I've heard that I can use function* with yield , or async-await in order to avoid callback hell - how can I implement them in this case? 编辑:我听说可以将function* with yieldasync-await ,以避免发生回调地狱-在这种情况下,如何实现它们?

You should use a Promise to do this. 您应该使用Promise来做到这一点。 Below is some code using bluebird which will do what you want. 以下是一些使用bluebird的代码,可以完成您想要的操作。 The Promise.any method will return the first successful call from the Array out of 10 tries. Promise.any方法将返回Array中10次尝试中的第一个成功调用。

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));

You have to understand that the asynchronous operations can not return a result right after their call. 您必须了解,异步操作在调用后不能立即返回结果。 They trigger some handler when they have executed. 它们执行后会触发一些处理程序。 You can/should use that entry point to initiate or continue your logic flow. 您可以/应该使用该入口点来启动或继续您​​的逻辑流程。

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

and you can chain as many such asynchronous calls as you need but you can not return a response in this manner. 并且您可以根据需要链接任意数量的此类异步调用,但不能以这种方式返回响应。

Also you might be interested in the concept of a Promise . 您也可能对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); }; 

You can use the async library. 您可以使用异步库。

you dont need to do a setInterval or any timer for that matter, just wait for the response. 您无需为此事做setInterval或任何计时器,只需等待响应即可。

specifically you can use async.waterfall for this, something like: 具体来说,您可以为此使用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)
  }
})

 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.stackoverflow&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.stackoverflow&client_id=3519&callback=?"}) console.log('Second call finished',response2); } main(); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 

In newer versions of nodejs you can use async await like the example above 在较新版本的nodejs中,您可以像上面的示例一样使用async await

Notice that $.ajax is not a node function. 请注意,$。ajax不是节点函数。 It is just for demonstration 只是为了示范

You can use await on any function that return a promise. 您可以在任何返回承诺的函数上使用await。 For the next example you need to install request package and use Node >= 8 for using promisify 对于下一个示例,您需要安装请求包并使用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 http://2ality.com/2017/05/util-promisify.html

https://github.com/request/request https://github.com/request/request

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM