如何使用 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 yield
或async-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();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
在较新版本的 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 同步运行两个函数?的主要内容,如果未能解决你的问题,请参考以下文章