getify / LABjs

Loading And Blocking JavaScript: On-demand parallel loader for JavaScript with execution order dependencies
http://labjs.com
2.28k stars 313 forks source link

Problem executing queue when script is blocked. #89

Closed ChristophBoget closed 10 years ago

ChristophBoget commented 11 years ago

I hope I explain this well enough. If you still have questions, please ask.

There appears to be an issue running the queue when a queued script is blocked (for whatever reason). When adding a script to the queue, it goes through script -> do_script (which sets up the call backs) -> request_script. When the script is ready or is executed, it calls advance_exec_cursor which does some checking of it's own and only when a script is ready, it advances it's cursor. The problem is that if any script breaks the chain and causes the cursor not to get advanced. From that point on the cursor is never changed and any anonymous functions that were added to the end of the queue will never be executed as a consequence.

One of the reasons that a script could be blocked is a browser extension. In my case, I have an external js file called popup2.js. Evidently, the AdBlock browser extension (at least in Safari) blocks this javascript file. Because of that, LAB gets all kinds of discombobulated. Consider the following test case:

http://jsfiddle.net/uTrFr/

Nothing special going on there. Just queueing up 5 scripts (one named popup2.js) and queueing an anonymous function. And there's nothing major going on in each of those scripts, just some console.logs. Please reference the attached images. One is what I see in Firefox (that doesn't have the AdBlock extension) and the other is what I see in Safari (that does have the extension and has it turned on; if I turn it off, everything works and the page behaves as it does in Firefox).

(Note: I modified the LAB.js file to log some information, as you can see).

Firefox: lab in firefox

Safari lab in safari

In the Firefox debug, you can see everything getting loaded properly and the cursor advancing as it should. But in the Safari debug, the cursor gets stuck at 1 because the popup2.js was blocked and it's callback was never called (because it was never marked as finished or ready).

There's nothing you can do to prevent a script from getting blocked (for whatever reason). But LAB should account for that possibility and not prevent any subsequent queued anonymous functions from executing.

Hopefully I've made the issue clear but again, if you have any questions or require any additional information, please do not hesitate to ask.

thnx, Christoph

getify commented 11 years ago

LABjs stands for "Loading and Blocking JS". By design, the library blocks execution of script B until after script A has successfully run, if you have specified via a wait() call in between the two script() calls that there is such an execution order dependency.

There's an implicit assumption with the usage of LABjs that if B depends on A, and you therefore use wait() to tell B not to execute until A completes, then B cannot and should not run if A doesn't successfully run, no matter what. So the question becomes, in your case, does B depend on A, or not?

If B does depend on A, then LABjs is doing the correct thing by not letting B run if A never runs. If B doesn't depend on A, then don't put B after A in a chain with a wait() in between them. If they are execution-order independent, then either load them in parallel (aka, with no wait() in between them) or load them in two separate $LAB chains.

ChristophBoget commented 11 years ago

Ok, go ahead and make sense why don't you!! :p

To answer your question, B does not depend on A. I'm not sure if you went to the fiddle page and saw the code, but the anonymous function is part of it's on separate $LAB.queueWait() call. The implication being that it should be queued and executed in turn but is not dependent upon the previous $LAB.queueScript() call. At least, that is my understanding of the documentation (which, I'm perfectly fine with admitting that understanding is flawed if, in fact, I'm incorrect).

Thanks for your reply!

thnx, Christoph

getify commented 11 years ago

The "queue" mechanism (queueScript() and queueWait()) constructs a single chain of loading and blocking. Making separate calls to the queue functions doesn't create separate internal queues.

If you need to create multiple queues, which it appears you're trying to do, you can do so manually rather than with the single queue mechanism, like this:

var q1 = [];
var q2 = [];

// later
q1.push("A.js",false,"C.js",function(){ alert("A and C done"); });

// later
q2.push("B.js","D.js",false,"E.js",function(){ alert("B, D, and E done"); });

function loadQueue(q) {
   var i, $chain = $LAB;
   for (i=0; i<q.length; i++) {
      if (typeof q[i] === "string") $chain = $chain.script(q[i]);
      else if (!q[i]) $chain = $chain.wait();
      else if (typeof q[i] === "function") $chain = $chain.wait(q[i]);
   }
}

// load separate queues independently
loadQueue(q1);
loadQueue(q2);

Basically, that's doing exactly what the internal built-in queue mechanism does, so if you create separate chains without any interdependency, they should behave basically independently.