我有一个非常基本的脚本,我一直在逐步添加功能 - 几乎没有优化。
它应该通过X brands
并为每个(每个都有一个唯一的端点)运行API调用,然后将结果保存为CSV。
CSV部分工作得很好,尽管速度有点慢。 (200KB文件约20秒)。
然而,当我把它全部包裹在brands
的循环中时,它现在超时了。默认情况下它会运行2分钟然后给我一个错误
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
我学会了追加--max_old_space_size=4000000
给它多余的记忆(它也死于2GB)。在4GB我没有得到错误,但我得到Windows内存泄漏警告,所以很明显我的脚本有问题。我觉得它在循环中循环,但不确定。
var http = require("https");
var qs = require("querystring");
var csvWriter = require('csv-write-stream');
var fs = require('fs');
var moment = require('moment');
let key = '';
var brands = ['REDACTED1','REDACTED2','REDACTED3','REDACTED4','REDACTED5','REDACTED6','REDACTED7','REDACTED8']
var dataRequest = function(token){
var date = new Date()-1; //Make it yesterday
var formattedDate = moment(date).format('YYYYMMDD');
var yesterdayDashes = moment(date).format('YYYY-MM-DD');
for (var k=0;k<=brands.length;k++){
var headers = [];
var csvBody = [];
var csvContent = "data:text/csv;charset=utf-8,";
var options = {
"method": "GET",
"hostname": "REDACTED",
"port": "443",
"path": "REDACTED/"+brands[k]+"/"+yesterdayDashes+"?REDACTED&access_token="+token,
"headers": {
"authentication": "Bearer "+token,
"content-type": "application/json",
"cache-control": "no-cache"
}
}
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
var object = JSON.parse(body);
var writer = csvWriter({
headers: ["REDACTED1", "REDACTED2", "REDACTED3", "REDACTED4", "REDACTED5", "REDACTED6", "REDACTED7", "REDACTED8", "REDACTED9"]
})
writer.pipe(fs.createWriteStream(brands[k] +'_' +formattedDate +'_DEMOGRAPHIC.csv'))
for (var i=0;i<object.length; i++){
writer.write([
object[i].field1.REDACTED1,
moment(object[i].optin.REDACTED2).format('YYYYMMDD HHMMSS'),
object[i].field2.REDACTED1,
object[i].field2.REDACTED2,
object[i].field2.REDACTED3,
object[i].field2.REDACTED4,
object[i].field2.REDACTED5,
object[i].field3.REDACTED6,
object[i].field3.REDACTED7
])
}
writer.end()
});
});
req.end();
}
}
var authToken = function(){
var form = qs.stringify({
grant_type: 'client_credentials',
client_credentials: 'client_id:client_secret',
client_id: 'cdg-trusted-client',
client_secret: 'REDACTED'
})
var options = {
"method": "POST",
"hostname": "REDACTED3",
"port": null,
"path": "REDACTED3",
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"cache-control": "no-cache"
}
};
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
key = json['access_token'];
});
});
req.write(form);
req.end();
dataRequest(key);
}
authToken();
我删除了敏感信息,但所有逻辑仍然存在。这是一个我快速拼凑的脚本,但老实说,我真的没有看到它需要这么多内存的任何理由。我想也许它是无限循环但直接在节点内测试每个循环我没有问题。
流程开始获取持有者令牌,然后将其传递给函数以提取数据。
当我在讨论把它放在CodeReview中时,这段代码根本不是技术。
更新
修改第一个for
循环现在立即输出unidentified
CSV并且不完全是其他任何内容。但是console.log(brands[k])
正在输出相应的文件。
更新2
我的JS调试版本在任何地方放置console.log()
,我注意到一旦我低于http.request
init,品牌[k]突然变得不明确。我认为这可能是由于没有传递给函数吗?
更新3
我的undefined
问题是由分号丢失引起的,早期结束了for循环。我已经纠正了它,但现在我又遇到了Max Stack Trace问题。
我的问题上下文现在似乎是“我如何使for
不能运行异步?”
答案 0 :(得分:3)
这是你的问题:
for (var k=0;k=brands.length;k++){
您使用了作为赋值运算符的运算符=
,并将品牌长度放入变量k
而不是<
运算符,以检查k
是否仍小于brands.length
。
在循环中测试的表达式是数字8(品牌数组的长度),它始终保持为8,因此您将进入无限循环。
请注意,对于布尔表达式,任何不同于0
的值都表示值true,而0
表示false。
这是关于无限循环问题,同时请注意,将代码传递给dataRequest
函数时,代码还有另一个问题。
问题是你试图在req.end()之后立即传递它;发送请求。
此时响应仍未存在,res.on("end", ...
的回调函数仍未执行。
基本上你将undefined传递给dataRequest函数,导致你的第二个错误。只需将dataRequest
的调用移至end
回调内,就像这样:
res.on("end", function () {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
key = json['access_token'];
dataRequest(key);
});
答案 1 :(得分:0)
var http = require("https");
var qs = require("querystring");
var csvWriter = require('csv-write-stream');
var fs = require('fs');
var moment = require('moment');
let key = '';
var brands = ['REDACTED1','REDACTED2','REDACTED3','REDACTED4','REDACTED5','REDACTED6','REDACTED7','REDACTED8']
var dataRequest = function(token, client){
var date = new Date()-1; //Make it yesterday
var formattedDate = moment(date).format('YYYYMMDD');
var yesterdayDashes = moment(date).format('YYYY-MM-DD');
var headers = [];
var csvBody = [];
var csvContent = "data:text/csv;charset=utf-8,";
var options = {
"method": "GET",
"hostname": "REDACTED",
"port": "443",
"path": "REDACTED/"+client+"/"+yesterdayDashes+"?REDACTED&access_token="+token,
"headers": {
"authentication": "Bearer "+token,
"content-type": "application/json",
"cache-control": "no-cache"
}
}; // <--- This was the culprit
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
var object = JSON.parse(body);
var writer = csvWriter({
headers: ["REDACTED1", "REDACTED2", "REDACTED3", "REDACTED4", "REDACTED5", "REDACTED6", "REDACTED7", "REDACTED8", "REDACTED9"]
})
writer.pipe(fs.createWriteStream(client +'_' +formattedDate +'_DEMOGRAPHIC.csv'))
for (var i=0;i<object.length; i++){
writer.write([
object[i].field1.REDACTED1,
moment(object[i].optin.REDACTED2).format('YYYYMMDD HHMMSS'),
object[i].field2.REDACTED1,
object[i].field2.REDACTED2,
object[i].field2.REDACTED3,
object[i].field2.REDACTED4,
object[i].field2.REDACTED5,
object[i].field3.REDACTED6,
object[i].field3.REDACTED7
])
}
writer.end()
});
});
req.end();
}
}
var authToken = function(){
var form = qs.stringify({
grant_type: 'client_credentials',
client_credentials: 'client_id:client_secret',
client_id: 'cdg-trusted-client',
client_secret: 'REDACTED'
})
var options = {
"method": "POST",
"hostname": "REDACTED3",
"port": null,
"path": "REDACTED3",
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"cache-control": "no-cache"
}
};
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
key = json['access_token'];
for (var k=0;k<=brands.length;k++){
var client = brands[k];
dataRequest(key, client);
});
});
req.write(form);
req.end();
}
authToken();
最后,如上所述,我使用k = x
时出现问题,一旦我将其修改为k < x
,我就已对代码进行了多次更改,从而导致上述更新。除k = x
之外,我上面的分号不合适,这打破了for
。
我还选择按brand
发起一个电话,而不是在品牌内发起。该剧本现在运作良好。
我已经添加了一个辅助答案,不会夸大问题并最终显示整个工作代码。