我想知道是否有办法让我在javascript中对每个堆栈使用一种“私有领域”或“私有内存”,以帮助我清除竞争条件,尤其是并行{{1调用。
例如,假设我有这个:
setTimeout
我将提出两个例外,但我不知道它对应哪个电话。
我可以实现一种私有领域的东西,但它会使代码复杂化,如果有的话,我宁愿使用本机解决方案。
答案 0 :(得分:2)
您可以使用错误对象的stack
属性(MDN)。
我更新了sweetamylase的小提琴:http://jsfiddle.net/robatwilliams/Am8mf/3/
两个记录的堆栈跟踪的最后一行将显示哪个setTimeout
启动了哪个错误:
Error: 1
at bar (http://fiddle.jshell.net/robatwilliams/Am8mf/3/show/:28:15)
at foo (http://fiddle.jshell.net/robatwilliams/Am8mf/3/show/:23:5)
at http://fiddle.jshell.net/robatwilliams/Am8mf/3/show/:35:26
Error: 2
at bar (http://fiddle.jshell.net/robatwilliams/Am8mf/3/show/:28:15)
at foo (http://fiddle.jshell.net/robatwilliams/Am8mf/3/show/:23:5)
at http://fiddle.jshell.net/robatwilliams/Am8mf/3/show/:36:26
答案 1 :(得分:2)
根据您的上下文,许多浏览器中的典型错误对象将包含其他详细信息以及“堆栈”属性...
例如,V8 Chrome / NodeJS错误(以及IE)具有“堆栈”属性,可以为您的环境提供更多上下文。
try {
throw new Error();
} catch(err) {
console.dir(err.stack.toString().split('\n'));
}
输出(Chrome):
0: "Error"
1: " at :3:9"
2: " at Object.InjectedScript._evaluateOn (:532:39)"
3: " at Object.InjectedScript._evaluateAndWrap (:491:52)"
4: " at Object.InjectedScript.evaluate (:410:21)"
输出(IE10):
0 : "Error",
1 : " at eval code (eval code:2:3)",
2 : " at Global code (Unknown script code:5:1)"
输出(Firefox):
0 : "@chrome://firebug/conte...mmandLineExposed.js:192",
1 : ""
在正确的JS文件/模块中,您将获得相关文件和行/ col ...
不同的浏览器会有自己的实现细节,但这应该为您提供所需的上下文...
您可能还希望使用名称补充您的功能......
setTimeout(function IPityTheFoo() { foo(); }, 10);
setTimeout(function DoingMyFooThing1() { foo(); }, 10);
答案 2 :(得分:1)
您可以将一些标识符传递到foo()
以跟踪。试试这个:
setTimeout(function () { foo(1); }, 10);
setTimeout(function () { foo(2); }, 10);
修改你的foo()
函数以接受id参数并传递它。
function foo(id) {
/* some statements */
bar(id);
}
function bar(id) {
try {
throw {
name: "Exception",
message: "oooh damn!" + id
}
} catch (e) {
console.error(e.name, e.message);
}
}
请参阅小提琴中的示例:http://jsfiddle.net/amyamy86/Am8mf/
所以,如果我这样做:
setTimeout(function () { foo(1); }, 10);
setTimeout(function () { foo(2); }, 10);
然后它回来了:
Exception oooh damn!1
Exception oooh damn!2
或者如果我这样做:
setTimeout(function () { foo(1); }, 10);
setTimeout(function () { foo(2); }, 9);
然后它回来了:
Exception oooh damn!2
Exception oooh damn!1
编辑#2 要不必传递 id 作为参数:
var currentId = null;
function foo() {
var id = currentId; // since it's copied over to 'id', we don't care about 'currentId' anymore
var bar = function() {
try {
throw {
name: "Exception",
message: "oooh damn!" + id
}
} catch (e) {
console.error(e.name, e.message);
}
}
/* some statements */
bar();
}
setTimeout(function () {
currentId = 1;
foo();
}, 10);
setTimeout(function () {
currentId = 2;
foo();
}, 10);
所以currentId
是一个共享变量,但它是在setTimeout()
结束时设置的,并执行该函数。
这样做:
setTimeout(function () {
currentId = 1;
foo();
}, 10);
setTimeout(function () {
currentId = 2;
foo();
}, 9);
然后它回来了:
Exception oooh damn!2
Exception oooh damn!1
答案 3 :(得分:1)
我可能会稍微使用闭包并避免使用全局变量。您可以将ID替换为允许您计算呼叫或处理竞争条件的上下文对象。
var invokeWithId = function(id, f){
return function(){
f.apply({id:id}, arguments);
}
}
setTimeout(invokeWithId(1, foo), 10);
答案 4 :(得分:1)
您可以修改 setTimeout 功能并汇总所有答案的体验。
var oldTimeout = window.setTimeout;
window.setTimeout = function(callback, delay){
if(callback.name == 'Trace'){
oldTimeout.apply(this,[function(){
try{
callback.apply(this,arguments);
}
catch(e){
e.message += ' ('+e.stack.split('\n').map(function(e){return '['+e.replace(location.href,'plain_script').replace('@',' in ')+']'}).join(' < ')+')';
throw e;
}
}].concat(Array.prototype.slice.call(arguments,1,arguments.length)));
}
else{oldTimeout.apply(this,arguments)};
}
答案 5 :(得分:1)
不使用try / catch,堆栈或修改现有代码,所有你可以使用更智能的setTimeout:
(function(){
if(!setTimeout.id){
var old=setTimeout, hits=0;
setTimeout=function timeout(code, delay, id){
var count=hits++;
setTimeout.calls=setTimeout.calls||{};
setTimeout.calls[id||count]=code;
return old(function(){
setTimeout.id=id||count;
code.call? code() : Function(code)();
}, delay);
};
setTimeout.id=true;
}//end setTimeout patcher
}());
function foo() { /* some statements */
bar();
}
function bar() { throw new Error("oooh damn! #"+setTimeout.id+"\t"+setTimeout.calls[setTimeout.id] ); }
setTimeout(function() { foo(); }, 20, "more");
setTimeout(function() { foo(); }, 10, "something");
setTimeout(function() { foo(); }, 20);
setTimeout(function() { foo(); }, 10);
基本上,这使得setTimeout的属性可用,id。因为JS是单线程的,所以在你的函数完成之前,这个属性不可能被覆盖,所以“time”设置setTimeout.id是一个超时函数运行的“当前”。
你可以传递第三个参数(忘记浏览器quirk currying)来手动指定ID,或者为其分配索引。这意味着每个调用都有一个唯一的标识符,您可能需要调试它。如果你需要代码本身,我的setTimeout提供了一个.calls属性,这个查找允许你查看在该ID下评估的代码/函数。