gpt4 book ai didi

Javascript 内存泄漏问题 - promise 和递归

转载 作者:行者123 更新时间:2023-12-03 11:47:19 26 4
gpt4 key购买 nike

我在这段代码中遇到了内存问题:

var RequestManager = function(customRequestArgs){

var requestManager = this;

this.customRequestArgs = customRequestArgs || [];



this.CustomRequest = function(url, data){

var requestDeferred = $.Deferred();

// set default xmlRequestArgs
var xmlRequestArgs = {
method : "GET",
url : url,
onload : function(response) {
requestDeferred.resolve(response.responseText);
},
onerror : function(response){
requestDeferred.reject('xmlRequest failed', response);
}
};
// set custom xmlRequestArgs
var i;
for(i in requestManager.customRequestArgs){
if(requestManager.customRequestArgs.hasOwnProperty(i)){
xmlRequestArgs[i] = requestManager.customRequestArgs[i];
}
}

// append data, depending on method
var d = [];
for(i in data){
if(data.hasOwnProperty(i)){
d.push(i+'='+encodeURIComponent(data[i]));
}
}
var dataString = d.join('&');

if(xmlRequestArgs.method.toLowerCase() === 'get'){
if(url.indexOf('?')>=0){
xmlRequestArgs.url = url+dataString;
}
else{
xmlRequestArgs.url = url+'?'+dataString;
}
}
if(xmlRequestArgs.method.toLowerCase() === 'post'){
xmlRequestArgs.data = dataString;
}


// run request
GM_xmlhttpRequest(xmlRequestArgs);

return requestDeferred;
};

this.BatchRequestRunner = function(args){

var maxParallelRequests = args.maxParallelRequests || 8;

var onEachStart = args.onEachStart || function(requestIndex, url){return undefined;}; // must return undefined or loader promise (i.e. for cached results)
var onEachSuccess = args.onEachSuccess || function(result, requestIndex, url){return result;}; // must return result or promise that resolves to result
var onEachError = args.onEachError || function(error, requestIndex, url){return error;}; // must return error or promise that resolves to error

var urlAr = args.urlAr || [];

var storeResults = args.storeResults || false;

var reversedUrlArClone = urlAr.slice(0).reverse();
var deferredAr = [];
var resultAr = [];
var errorAr = [];


var runnerMethod = function(){

if(reversedUrlArClone.length > 0){

// get request url
var url = reversedUrlArClone.pop();

// get urlIndex (i-th url in urlAr)
var requestIndex = urlAr.length - reversedUrlArClone.length - 1;


// run onEachStart
$.when(onEachStart(requestIndex, url)).then(function(loaderPromise){

if(loaderPromise === undefined){

// set loaderPromise
loaderPromise = requestManager.CustomRequest(url);

}

var generateOnSuccess = function(requestIndex){
return function(result){


$.when(onEachSuccess(result, requestIndex, url)).then(function(result){

// store result
if(storeResults){
resultAr[requestIndex] = result;
}

// resolve deferredAr[requestIndex]
deferredAr[requestIndex].resolve();

// start runnerMethod for next request
runnerMethod();

});

};
};
var generateOnError = function(requestIndex){
return function(error){

$.when(onEachError(error, requestIndex, url)).then(function(error){

// store error
errorAr[requestIndex] = error;

// reject deferredAr[requestIndex]
deferredAr[requestIndex].reject();

// start runnerMethod for next request
runnerMethod();


});

};
};

// handle loader
loaderPromise.done(generateOnSuccess(requestIndex));
loaderPromise.fail(generateOnError(requestIndex));

});

}

};

var startParallelRequestThread = function(){
runnerMethod();
};

var start = function(){
var i,
runnerDeferred = $.Deferred();

// setup deferredAr
for(i=0;i<urlAr.length;i++){
deferredAr.push($.Deferred());
}

// setup onSuccess
$.when.apply($, deferredAr)
.done(function(){
runnerDeferred.resolve(resultAr);
})
// setup onError
.fail(function(){
runnerDeferred.reject(errorAr);
});

// start requestThreads
for(i=0;i<maxParallelRequests;i++){
startParallelRequestThread();
}

return runnerDeferred;
};


return {
start : start
};

};



return {
BatchRequestRunner : this.BatchRequestRunner,
CustomRequest : this.CustomRequest,
};
};

它应该是一个执行批量请求的类。用户可以设置默认请求参数(附加 header 等)和一堆批处理设置。

虽然代码按预期执行,但浏览器在一段时间后崩溃。检查任务管理器显示选项卡的进程占用了越来越多的内存。我一直试图寻找其中的原因,但一直未能找到。请问有人有什么想法吗?

如果我可以澄清任何问题,请告诉我。

问候,klmdb

最佳答案

好吧,我想我已经仔细考虑了代码,而且看起来您跳过了一些不必要的麻烦。主要通过使用两个标准技巧可以大大简化代码:

  • 使用 $.extend()(在两个地方),避免了手动循环对象的需要。
  • 使用 Array.prototype.reduce() 将数组转换为 .then() 链来代替“递归”。

以下版本的其他功能包括:

  • 结果和错误通过 Promise 链传递,而不是累积在外部数组中。
  • requestIndex 的需求(在很多地方)消失了,维护它的显式闭包的需求也消失了。
  • 不会创建延迟对象,这应该有助于减少可执行文件对内存的占用。
  • 现在,在调用 RequestManager() 时,
  • new 是可选的。原始代码对于 new 是否有意为之并不明确。

这是简化版本...

var RequestManager = function(customRequestArgs) {
var CustomRequest = function(url, data) {
//GM_xmlhttpRequest is assumed to call $.ajax() (or one of its shorthand methods) and return a jqXHR object
return GM_xmlhttpRequest($.extend({ //$.extend() replaces several lines of original code
method: "GET",
url: url,
data: data
}, customRequestArgs || {})).then(function(response) {
return response.responseText;
}, function(jqXHR, textStatus, errorThrown) {
return ('xmlRequest failed: ' + textStatus);
});
};
//Defaults are best defined (once per RequestManager) as an object, which can be extended with $.extend().
var batchRequestDefaults = {
maxParallelRequests: 8,
onEachStart: function(url) { return undefined; }, // must return undefined or loader promise (i.e. for cached results)
onEachSuccess: function(result, url){ return result; }, // must return result or promise that resolves to result
onEachError: function(error, url){ return error; }, // must return error or promise that resolves to error.
urlAr: [],
storeResults: false
};
var BatchRequestRunner = function(args) {
args = $.extend({}, batchRequestDefaults, args); //$.extend() replaces several lines of original code
function runnerMethod(index, urlAr) {
//Note recursion is avoided here by the use of .reduce() to build a flat .then() chain.
return urlAr.reverse().reduce(function(promise, url) {
var requestIndex = index++;
return promise.then(function(result1) {
return $.when(args.onEachStart(requestIndex, url)).then(function(p) {
return (p === undefined) ? CustomRequest(url) : p;
}).then(function(result2) {
args.onEachSuccess(result2, requestIndex, url);
// No return value is necessary as result2 is assumed
// to be fully handled by onEachSuccess(),
// so doesn't need to be passed down the promise chain.
}, function(error) {
// This is messy but :
// (a) is consistent with the stated rules for writing onEachError() functions.
// (b) maintains the original code's behaviour of keeping going despite an error.
// This is achieved by returning a resolved promise from this error handler.
return $.when(args.onEachError(error, requestIndex, url)).then(function(error) {
return $.when(); //resolved promise
});
});
});
}, $.when());
}
var start = function() {
// start requestThreads
var i, promises = [],
pitch = Math.ceil(args.urlAr / args.maxParallelRequests),
startIndex, endIndex;
for(i=0; i<args.maxParallelRequests; i++) {
startIndex = pitch * i;
endIndex = pitch * (i + 1) - 1;
promises.push(runnerMethod(startIndex, args.urlAr.slice(startIndex, endIndex)));
}
// Note: Results and errors are assumed to be fully handled by onEachSuccess() and onEachError() so do not need to be handled here or passed on down the promise chain.
return $.when.apply(null, promises);
};
return {
start: start
};
};
return {
BatchRequestRunner: BatchRequestRunner,
CustomRequest: CustomRequest
};
};

未经测试,因此可能需要调试

到目前为止,最难的方面是错误的处理。原始代码在这方面有相当奇怪的行为,我尝试通过使用人造(不间断)错误来模拟。凌乱,但清除了递归后,我想不出另一种方法来做到这一点。

除了我的错误之外,行为上的唯一区别应该是 start() 返回的 promise ,它现在将提供一个结果数组和一个(人造)错误数组,捆绑到一个 js 普通对象。这与 runnerMethod 尽管出现错误仍继续运行是一致的。

现在结果是通过 promise 链传递的,“storeResults”已经消失了。我看不出有任何理由想要使用 storeResults === true 之外的任何东西来运行。

我唯一的(?)假设是 $ 是 jQuery 并且 GM_xmlhttpRequest 使用 jQuery.ajax() 并返回(或者可以是返回)其jqXHR对象。从我看来,这似乎是合理的。如果假设无效,那么您将需要恢复该代码部分。

有关进一步的说明,请参阅代码中的注释。

调试时,如果它仍然崩溃,那么我建议它只是内存不足,而不是泄漏本身

编辑

阅读(在下面的评论中)批处理和 onEachError() 等的描述后,start()runnerMethod() 已经上面已经编辑过。

变更摘要:

  • 批处理定义:start() 现在通过将 urlAr切片传递给 runnerMethod() 来启动 8 个并行批处理。
  • requestIndex:以非常简单的方式恢复。

编辑版本的行为与问题中的原始代码类似但不完全相同。不同之处在于每个批处理都是预定义的,而不是响应式的。

最终,如果此版本内存消耗较少并且实际上运行完成(这就是练习的目标),则删除响应行为可能是值得付出的代价。

要查看未编辑的代码,请查看问题的编辑历史记录

关于Javascript 内存泄漏问题 - promise 和递归,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25996400/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com