0

I wish to know if there was a way for me to use a kind of "private realm" or "private memory" to each stack in javascript, to help me clear out racing condition in especially the case of parallel setTimeout calls.

For instance, let's say I have this:

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

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

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

I'm going to have 2 exceptions raised, but I won't know to which call it corresponds.

I could implement a kind of private realm thing but it would really complicate the code and I'd rather use native solutions if there were any.

Sebas
  • 21,192
  • 9
  • 55
  • 109
  • Why wouldn't the first `setTimeout` code execute first? – Ian Mar 28 '13 at 17:38
  • it does execute first, but its execution might be delayed so the second one might raise the exception first – Sebas Mar 28 '13 at 17:41
  • You mean like if the `foo` function has something asynchronous inside of it? Otherwise, it shouldn't be delayed compared to the second. Since they reference the same function (`foo`), the code executed is the same, so there's no reason the first `foo();` should ever execute after the second `foo();` (again, unless there's something asynchronous in it). Now, this would be different if the first `setTimeout` called `asdf()` and the second called `foo()`. I wasn't sure if you were being very specific or broad. Just trying to understand the question better :) – Ian Mar 28 '13 at 17:43
  • yes, the "/* some statements */" are doing relative things. It might not be the same at all in the 2 calls. The whole thing in a badly async context. – Sebas Mar 28 '13 at 17:45

6 Answers6

2

You can use the Error object's stack property (MDN).

I updated sweetamylase's fiddle: http://jsfiddle.net/robatwilliams/Am8mf/3/

The last line of the two logged stack traces will show you which setTimeout kicked off which error:

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 
rwm
  • 251
  • 2
  • 5
  • your answer is correct, however I should have mentionned that the calls to foo() could also be from the same line (example: you click twice a button and inside there's an ajax call each time). The question was unclear about it, thank you for your input. – Sebas May 04 '13 at 23:49
2

Depending on your context, a typical Error object in many browsers will have additional details, as well as a "stack" property...

For example, V8 Chrome/NodeJS errors (as well as IE) have a "stack" property that can give you more context into your environment.

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

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

Output (IE10):

 0 : "Error",
    1 : "   at eval code (eval code:2:3)",
    2 : "   at Global code (Unknown script code:5:1)"

Output (Firefox):


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

Inside of a proper JS file/module, you will get the relevant file and line/col...

Different browsers will have their own implementation details, but this should give you the context you are looking for...

You may also wish to supplement your functions with a name...

setTimeout(function IPityTheFoo() { foo(); }, 10);
setTimeout(function DoingMyFooThing1() { foo(); }, 10);
Tracker1
  • 19,103
  • 12
  • 80
  • 106
  • Thank you for your input, see my comment on rwm's answer – Sebas May 04 '13 at 23:50
  • @Sebas, As for input order, you could attach an id to each function for that... incrementing with each attachment. The named function would let you use .toString() on the function reference to access the name.. ymmv though. Good luck to you, there are lots of ways to skin this particular cat. – Tracker1 May 09 '13 at 18:14
1

You can pass in some identifier into your foo() to keep track. Try this:

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

And modify your foo() function to accept the id argument and pass it around.

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);
    }
}

See example in fiddle: http://jsfiddle.net/amyamy86/Am8mf/

So, if I do:

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

Then it comes back:

Exception oooh damn!1
Exception oooh damn!2

Or if I do:

setTimeout(function () { foo(1); }, 10);
setTimeout(function () { foo(2); }, 9);

Then it comes back:

Exception oooh damn!2
Exception oooh damn!1

Edit #2 To not have to pass an id as argument:

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

So the currentId is a shared variable, but it is set at the moment of when the setTimeout() is over, and executes the function.

So doing:

setTimeout(function () {
    currentId = 1;
    foo();
}, 10);
setTimeout(function () {
    currentId = 2;
    foo();
}, 9);

Then it comes back:

Exception oooh damn!2
Exception oooh damn!1
Amy
  • 7,388
  • 2
  • 20
  • 31
  • yes this is the idea, fixing a call stack id to it. But I don't want to be so instrusive, foo and bar are examples, it could be any function called, and I don't want to add a parameter to each of them – Sebas Mar 28 '13 at 17:49
  • @Sebas Just added an example where you can share the `currentId` variable between the two invocations of `foo()`, but it also means that `currentId` needs to be in the same scope. – Amy Mar 28 '13 at 18:02
  • since currentId is global, if the first call crashes after the 2nd one is done i'll have currentId = 2 instead of 1, this is erroneous. That's the whole problem. – Sebas Mar 28 '13 at 18:02
  • @Sebas I see what you mean, so `foo` should keep reference of the id rather than using the global... `var id = currentId;` inside `foo()` and use `id` for when `bar()` throws the exception. Either `bar(id)` or just `bar()` (but it needs to have visibility to the `id` variable.) – Amy Mar 28 '13 at 18:05
  • yes exactly, it would be like having a global closure. I could do it like I said, but.. really, i wish javascript had a way to do it natively – Sebas Mar 28 '13 at 18:20
  • @Sebas There is a way of modifying the prototype, eg. for `foo`, and add your own `id` in that? But generally I think mucking with the prototype is hard to debug and flaky at best, more transparent ways like this seems better. But if you want to explore that, have a look here: http://stackoverflow.com/questions/1997661/unique-object-identifier-in-javascript – Amy Mar 28 '13 at 18:23
  • but the prototype is shared isn't it? would be the same problem – Sebas Mar 28 '13 at 18:28
  • @Sebas Agreed, since you're not creating objects of foo, it wouldn't work too well eitherway – Amy Mar 28 '13 at 18:32
1

I would probably use closure a little differently and avoid a global variable. You could replace ID with some context object that allows you to count calls or handle race conditions.

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

http://jsfiddle.net/j8hgE/

pcw216
  • 126
  • 2
  • Even though what you propose is not an automatic way of doing it from the native javascript engine, and also since (apparently) there's no such a possibility, your answer is the closest to what I need. I'd pass a timestamp as a parameter so I know which one was called first. – Sebas May 04 '13 at 23:56
  • @Sebas It's worth noting that the time in JS isn't that fine grained, and if you queue up two items quickly there's a chance that both will have the same timestamp... you may want to use an id for each request with a ++my_id_variable. – Tracker1 May 09 '13 at 18:12
1

You can modify your setTimeout function and aggregate experience of all answers.

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/

3y3
  • 802
  • 1
  • 6
  • 18
1

without using try/catch, stacks, or modifying existing code, all you can so is use a smarter 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);

basically, this makes a property of setTimeout available, id. since JS is single-threaded, it SHOULD be impossible for this property to get over-written before your function completes, so the "last" set setTimeout.id is the "current" one while a timed-out function runs.

you can pass a third argument (forget browser-quirk currying for this) to manually specify an ID, or let an index be assigned to it. This means each call has a unique identifier, which might be all you need to debug. If you need the code itself, my setTimeout provides a .calls property, a lookup that that lets you view the code/function evaluated under that ID.

dandavis
  • 16,370
  • 5
  • 40
  • 36
  • thank you for your input, this is an interesting approach also. Identifying them from their calling sequence – Sebas May 04 '13 at 23:53