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.

4
  • Why wouldn't the first setTimeout code execute first? Commented Mar 28, 2013 at 17:38
  • it does execute first, but its execution might be delayed so the second one might raise the exception first Commented Mar 28, 2013 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 :) Commented Mar 28, 2013 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. Commented Mar 28, 2013 at 17:45

6 Answers 6

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 
Sign up to request clarification or add additional context in comments.

1 Comment

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

2 Comments

Thank you for your input, see my comment on rwm's answer
@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.
1
+50

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/

2 Comments

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 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.
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

8 Comments

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 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.
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 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.)
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
|
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/

1 Comment

interesting approach: you would identify from the arguments point of view
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.

1 Comment

thank you for your input, this is an interesting approach also. Identifying them from their calling sequence

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.