如何让Node.js循环中的函数同步执行?附代码示例
解决Node.js循环内异步函数同步执行的问题
嘿,我完全懂你现在的困扰——Node.js的异步特性在循环里很容易打乱执行顺序,而且你看到所有sequence都显示3的问题,是var的变量提升在搞鬼。别担心,我们有几种简单有效的方法来实现你想要的post request → 响应 → 计数 → end of function的执行顺序。
问题根源
你的sendPost是异步函数,循环会先快速跑完所有3次迭代,把3个异步请求都发出去,然后才会陆续执行回调。再加上var i是函数级作用域,所有回调共享同一个i变量,当回调执行时循环已经结束,i的值已经变成3,所以所有回调里的sequence都是3。
方法一:使用async/await(最推荐的现代方案)
这种方法让异步代码看起来像同步代码,逻辑清晰易维护。我们需要先把sendPost改成返回Promise的版本,再用async/await控制循环顺序。
步骤1:改造sendPost为Promise版本
const sendPost = function(data) { return new Promise((resolve, reject) => { console.log("post request :: " + JSON.stringify(data)); const formData = JSON.stringify(data); request({ headers: { 'Content-Type': 'application/json' }, uri: "https://someurl/Login", body: formData, method: 'POST' }, function(error, response, body) { if (!error && response.statusCode == 200) { resolve(body); // 请求成功时返回响应数据 } else { console.log(error); reject(error); // 请求失败时抛出错误 } }); }); };
步骤2:改造abc为async函数,使用await控制顺序
const abc = async (count) => { // 用let声明i,块级作用域保证每个迭代的i都是独立值 for (let i = 0; i < count; i++) { const data = { "userName": "admin@xyz.com", "password": "xyz123#" }; try { // await会等待sendPost的Promise完成后才继续下一次循环 const responseData = await sendPost(data); console.log(responseData); console.log("sequence " + i); } catch (err) { // 捕获请求失败的错误 console.log(err); } } // 所有循环执行完后才会输出这句话 console.log("end of function"); }; abc(3);
这样执行顺序就完全符合你的期望:每一次循环都会先发送请求,等待响应返回后打印数据和序列数,再进入下一次循环,直到3次都完成后才输出end of function。
方法二:Promise链式调用(递归方式)
如果你不想用async/await,可以用递归的方式串联Promise,保证前一个请求完成后再执行下一个。
const abc = (count) => { let i = 0; const nextRequest = () => { if (i >= count) { console.log("end of function"); return; } const data = { "userName": "admin@xyz.com", "password": "xyz123#" }; sendPost(data) .then(responseData => { console.log(responseData); console.log("sequence " + i); i++; nextRequest(); // 完成当前请求后,执行下一个 }) .catch(err => { console.log(err); i++; nextRequest(); // 即使失败,也继续下一个请求 }); }; nextRequest(); // 启动第一个请求 };
为什么原来的var i会全显示3?
var是函数级作用域,循环里的i其实是同一个变量。当循环快速跑完3次后,i的值已经变成3,这时候异步请求的回调才开始执行,所以所有回调里拿到的i都是3。换成let就解决了这个问题,因为let是块级作用域,每个循环块里的i都是独立的,保存了当前迭代的数值。
内容的提问来源于stack exchange,提问作者rahul
相关产品推荐
相关产品推荐

