0

我想知道是否有办法让我在 javascript 中对每个堆栈使用一种“私有领域”或“私有内存”,以帮助我清除竞速条件,尤其是在并行setTimeout调用的情况下。

例如,假设我有这个:

function foo() { 
    /* some statements */ 
    bar(); 
} 

function bar() { throw new Exception("oooh damn!"); }

setTimeout(function() { foo(); }, 10);
setTimeout(function() { foo(); }, 10);

我将提出 2 个异常,但我不知道它对应于哪个调用。

可以实现一种私有领域的东西,但它确实会使代码复杂化,如果有的话,我宁愿使用本机解决方案。

4

6 回答 6

2

您可以使用 Error 对象的stack属性 ( MDN )。

我更新了甜淀粉酶的小提琴: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 
于 2013-04-30T22:24:30.580 回答
2

根据您的上下文,许多浏览器中的典型错误对象将具有其他详细信息,以及“堆栈”属性...

例如,V8 Chrome/NodeJS 错误(以及 IE)具有“堆栈”属性,可以为您提供更多环境上下文。

try { 
  throw new Error();
} catch(err) { 
  console.dir(err.stack.toString().split('\n'));
}

输出(铬):

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)"

输出(火狐):


    0 : "@chrome://firebug/conte...mmandLineExposed.js:192",
    1 : ""

在适当的 JS 文件/模块中,您将获得相关文件和行/列...

不同的浏览器会有自己的实现细节,但这应该会给你你正在寻找的上下文......

您可能还希望用名称来补充您的功能......

setTimeout(function IPityTheFoo() { foo(); }, 10);
setTimeout(function DoingMyFooThing1() { foo(); }, 10);
于 2013-04-30T23:02:07.650 回答
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
于 2013-03-28T17:47:52.533 回答
1

我可能会稍微不同地使用闭包并避免使用全局变量。您可以将 ID 替换为一些允许您计算调用或处理竞争条件的上下文对象。

var invokeWithId = function(id, f){
    return function(){               
        f.apply({id:id}, arguments);
    }
}
setTimeout(invokeWithId(1, foo), 10);

http://jsfiddle.net/j8hgE/

于 2013-04-30T18:09:03.283 回答
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)};
}

http://jsfiddle.net/RSbtF/1/

于 2013-05-01T19:29:51.863 回答
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 是单线程的,所以在你的函数完成之前,这个属性应该不可能被覆盖,所以当超时函数运行时,“最后”设置 setTimeout.id 是“当前”设置。

你可以传递第三个参数(忘记浏览器的怪癖柯里化)来手动指定一个 ID,或者让一个索引分配给它。这意味着每个调用都有一个唯一的标识符,这可能就是您需要调试的全部内容。如果您需要代码本身,我的 setTimeout 提供了一个 .calls 属性,这是一个查找,可让您查看在该 ID 下评估的代码/函数。

于 2013-05-01T20:58:57.657 回答