Javascript EventLoop

console.log('1 ' + new Date());
setTimeout(function() {
  console.log('2 ' + new Date());
}, 1000)
console.log('3 ' + new Date());
1 Fri Jun 05 2015 17:29:25 GMT+0200 (CEST)
3 Fri Jun 05 2015 17:29:25 GMT+0200 (CEST)
2 Fri Jun 05 2015 17:29:26 GMT+0200 (CEST)
var sleep = function(seconds) {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "http://127.0.0.1:8080/sleep.php?seconds=" + seconds, false);
  xhr.send(null);
};
<?php
//sleep.php
sleep($_GET['seconds']);
console.log(new Date());
sleep(2);
console.log(new Date());
Fri Jun 05 2015 17:24:19 GMT+0200 (CEST)
Fri Jun 05 2015 17:24:21 GMT+0200 (CEST)
console.log('1 ' + new Date());
setTimeout(function() {
  console.log('2 ' + new Date());
}, 1000)
sleep(2);
console.log('3 ' + new Date());
1 Fri Jun 05 2015 17:29:25 GMT+0200 (CEST)
3 Fri Jun 05 2015 17:29:27 GMT+0200 (CEST)
2 Fri Jun 05 2015 17:29:27 GMT+0200 (CEST)
console.log('1 ' + new Date());
setTimeout(function() {
  console.log('2 ' + new Date());
}, 1000)
setTimeout(function() {
  console.log('3 ' + new Date());
}, 500)
sleep(2);
1 Fri Jun 05 2015 17:30:34 GMT+0200 (CEST)
3 Fri Jun 05 2015 17:30:37 GMT+0200 (CEST)
2 Fri Jun 05 2015 17:30:37 GMT+0200 (CEST)
console.log('1 ' + new Date());
setTimeout(function() {
  console.log('2a ' + new Date());
  sleep(1)
  console.log('2b ' + new Date());
}, 0);
setTimeout(function() {
  console.log('3a ' + new Date());
  sleep(1)
  console.log('3b ' + new Date());
}, 0);
1 Fri Jun 05 2015 17:42:47 GMT+0200 (CEST)
2a Fri Jun 05 2015 17:42:49 GMT+0200 (CEST)
2b Fri Jun 05 2015 17:42:50 GMT+0200 (CEST)
3a Fri Jun 05 2015 17:42:50 GMT+0200 (CEST)
3b Fri Jun 05 2015 17:42:51 GMT+0200 (CEST)
setTimeout(function() {});
setInterval(function() {});


document.getElementById('foo').addEventListener('click', function() {});


var i = new Image();
i.onload = function() {});


$('.class').on('click', function() {});


xhr.open("GET", "/foo/bar", true);
xhr.onload = function (e) {};
xhr.onerror = function (e) {};

Limitations

var i1 = new Image();
var i2 = new Image();

var loadingCount = 2;

function oneLoaded(img)
{
  console.log('One image is loaded');
  if (--loadingCount === 0) {
     allLoaded();
  }   
}

function allLoaded() 
{
   console.log('All images are loaded');
}

i1.onload = oneLoaded;
i2.onload = oneLoaded;
function imageSize(url)
{
   var i = new Image();
   i.src = url;

   return [i.width, i.height];
}
function imageSize(url)
{
   var i = new Image();
   i.src = url;
   i.onload(function() {
     ???
   });

   return ???
}
function imageSize(url, callback)
{
   var i = new Image();
   i.src = url;
   i.onload(
      callback([i.width, i.height]);
   });
}

Promises

  • ES6
  • Q
  • AngularJs
var promise = new Promise(function(resolve, reject) {
  // ...

  if (itWorks) {
    resolve(myData);
  }
  else {
    reject(Error("Oh snap"));
  }
});
promise.then(function(result) {
  console.log(result);
}, function(err) {
  console.log(err);
});

How it works?

function callMeMayBe()
{
  return new Promise(function(resolve, reject) {
    var delay = 2000 * Math.random();
    setTimeout(function() {
       var success = Math.random() < 0.5;
       if (success) {
         resolve();
       } else {
         reject();
       }
    }, delay);
  });
}

callMeMayBe().then(function(result) {
  console.log('Hello');
});

Sample

function oneLoaded()
{
  console.log('One image is loaded');
}

function allLoaded()
{
  console.log('All images are loaded');
}

var i1 = new ImageWithPromise();
var i2 = new ImageWithPromise();

i1.$promise.then(oneLoaded);
i2.$promise.then(oneLoaded);

Promise.all([i1.$promise, i1.$promise]).then(allLoaded);

Promises

function imageSize(url)
{
   var i = new Image();
   i.src = url;

  return new Promise(function(resolve, reject) {
    i.onload(function() {
      resolve([i.width, i.height]);
    });
    
    i.onerror(function() {
      reject('Oh snap!');
    });
  }
}

Promises

imageSize(url).then(function(size) {
  console.log(size);
}).catch(function(err) {
  console.log(err);
});
$httpProvider.interceptors.push(function ($q, $injector) {
  return {
    responseError: function (response) {
      if (response.status === 401) {
        return $injector.get('authPrompt').retryHttp(response.config);
      }

      return $q.reject(response);
    }
  };
});

Advanced usage

$http.get('/someUrl').
  success(function(data, status, headers, config) {
    // this callback will be called asynchronously
    // when the response is available
  }).
  error(function(data, status, headers, config) {
    // called asynchronously if an error occurs
    // or server returns response with an error status.
  });

authPrompt

.factory('authPrompt', function ($auth, $mdDialog, $q, $http) {
  var prompting = null;
  return {
    retryHttp: function (request) {
      var deffered = $q.defer();

      this.login().then(function () {
        $http(request).then(function (data) {
          deffered.resolve(data);
        }, function (reason) {
          deffered.reject(reason);
        });
      }, function (reason) {
        deffered.reject(reason);
      });

      return deffered.promise;
    },
    login: ...
});

authPrompt

.factory('authPrompt', function ($auth, $mdDialog, $q, $http) {
  var prompting = null;
  return {
    retryHttp: ...
    login: function () {
      if (null !== prompting) {
        return prompting;
      }

      prompting = $mdDialog.show({
        controller: function ($scope) {
          $scope.authenticate = function (provider) {
            $auth.authenticate(provider).then(function () {
              $mdDialog.hide();
            });
          };
          $scope.cancel = $mdDialog.cancel;
        },
        templateUrl: 'templates/auth/popin/login.tpl.html'
      })['finally'](function () {
        prompting = null;
      });

      return prompting;
    }
  };
});

Question ?

Javascript EventLoop

By Jérémy DERUSSÉ