Примерно тоже самое находиться в jQuery и Dojo, но здесь сильно упрощенный вариант (не продумана вероятность ошибок в обработчиках, нельзя указать контекст выполнения обработчиков и т. д.). Предназначен, чтобы разобраться, как это работает на простом примере и/или для допиливания (выкладывайте прямо здесь, что получается). Критика приветствуется.
/**
* Id: deferred
* */
(function() {
var arraySlice = [].slice;
function Deferred() {
if (!(this instanceof Deferred)) {
throw new TypeError();
}
this._then = [];
this.then.apply(this, arguments);
}
Deferred.prototype = {
_resolved: false,
then: function _(callback) {
if (callback) {
if (this._resolved) {
callback();
} else {
this._then.push(callback);
}
}
if (arguments.length > 1) {
_.apply(this, arraySlice.call(arguments, 1));
}
return this;
},
resolve: function() {
if (!this._resolved) {
this._resolved = true;
var results = [], then = this._then, i = 0, l = then.length;
while (i < l) {
results[i] = then[i++].apply(null, arguments);
}
return results;
}
}
};
Deferred.when = function() {
var dfr = new Deferred(), count = 0;
function resolveFunc() {
if (!--count) {
dfr.resolve();
}
}
for (var i = 0, l = arguments.length; i < l; ++i) {
if (arguments[i]) {
++count;
arguments[i].then(resolveFunc);
}
}
return dfr;
};
this.Deferred = Deferred;
})();
// пример использования:
var dfr1 = new Deferred(function() { console.log('dfr 1'); });
var dfr2 = new Deferred(function() { console.log('dfr 2'); });
Deferred.when(dfr1, dfr2).then(function() { console.log('dfr all'); });
setTimeout(function() { dfr1.resolve(); }, 2000);
setTimeout(function() { dfr2.resolve(); }, 5000);
UPD: полезные ссылки:
1,
2,
3,
4,
5.