The Web is asynchronous. Many different types of components make up a system, and each one loads separately. Each component has a different way of loading, and a different way of notifying that it has loaded. Sometimes, it isn?t enough to have the component fully loaded, since affects other components on the page, and we may not always have a way of knowing about it.

Common object types are images, CSS files, JavaScript files, and XMLHTTP Requests. Each has an event to notify us that it has loaded, but having loaded does not mean that the component has begun to affect our page.

For example:

  • Load a CSS file in runtime and appending it to the page does not mean it will immediately affect its elements.
  • Receiving HTML code via an XMLHTTP request and appending it to the page does not mean that it will all be immediately rendered and that its components are accessible.
  • Loading a JavaScript file does not mean all of the objects it contains are ready for use.

For these extreme cases, I?ve written an elementary function which waits for a certain procedure to complete and only then executes further code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/// $waitUntil
///      waits until a certain function returns true and then executes a code. checks the function periodically
/// parameters
///      check - a function that should return false or true
///      onComplete - a function to execute when the check function returns true
///      delay - time in milliseconds, specifies the time period between each check. default value is 100
///      timeout - time in milliseconds, specifies how long to wait and check the check function before giving up
function $waitUntil(check,onComplete,delay,timeout) {
  // if the check returns true, execute onComplete immediately
  if (check()) {
      onComplete();
      return;
  }

  if (!delay) delay=100;

  var timeoutPointer;
  var intervalPointer=setInterval(function () {
      if (!check()) return; // if check didn't return true, means we need another check in the next interval

      // if the check returned true, means we're done here. clear the interval and the timeout and execute onComplete
      clearInterval(intervalPointer);
      if (timeoutPointer) clearTimeout(timeoutPointer);
      onComplete();
  },delay);
  // if after timeout milliseconds function doesn't return true, abort
  if (timeout) timeoutPointer=setTimeout(function () {
      clearInterval(intervalPointer);
  },timeout);
}

POC

1
2
3
4
5
6
7
8
9
10
11
12
13
var globalVariable=0;

setTimeout(function () { globalVariable=1; },2000);

$waitUntil(
  function () {
      console.log("checking globalVariable="+globalVariable);
      return globalVariable==1;
  },
  function () {
      alert("done!");
  }
);

In the example, we can see there is a variable named globalVariable initialized to 0, and that is manually set to 1 after 2 seconds. The function $waitUntil takes two functions. One will return true / false according to tests it will carry out, and the other will execute only after the first function returns true.

Examples of more common uses:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ajax("url.htm",function (source) {
  element.innerHTML=source;
  $waitUntil(
      function () { return document.getElementById("some-id-in-source")!=null; },
      function () { /* source is rendered and #some-id-in-source is available */ }
  );
});

loadCss("file.css",function () {
  $waitUntil(
      // element is an element whose height affected by a rule in file.css
      function () { return element.offsetHeight>0; },
      function () { /* css is applied on this element */ }
  );
});

loadJs("file.js",function () {
  $waitUntil(
      function () { return typeof(SomeTypeInFileJs)!="undefined"; },
      function () { /* SomeTypeInFileJs is ready to use */ }
  );
});

The ajax, loadCSS and loadJS functions asynchronously load files and notify that they have loaded. Their implementation can be found in any self-respecting JavaScript library.

Comments