blob: 1a2026cb7e2bf363dcd85ad6ca25db06919eb925 [file] [log] [blame]
/*******************************************************************************
* @license
* Copyright (c) 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution
* License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html).
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
/*global define*/
(function() {
var global = this;
if (!global.define) {
global.define = function(f) {
global.orion = global.orion || {};
global.orion.Deferred = f();
delete global.define;
};
}
}());
define(function() {
var head, tail, remainingHead, remainingTail, running = false;
function queueNotification(notify) {
if (running) {
if (!head) {
head = {
notify: notify,
next: null
};
tail = head;
} else {
tail.next = {
notify: notify,
next: null
};
tail = tail.next;
}
return;
}
running = true;
do {
while (notify) {
notify();
if (remainingHead) {
if (!head) {
head = remainingHead;
} else {
tail.next = remainingHead;
}
tail = remainingTail;
}
if (head) {
remainingHead = head.next;
remainingTail = tail;
notify = head.notify;
head = tail = null;
} else {
notify = null;
}
}
running = false;
} while (running);
}
function Deferred() {
var result, state, head, tail;
function notify() {
while (head) {
var listener = head;
head = head.next;
var deferred = listener.deferred;
if (listener[state]) {
try {
var listenerResult = listener[state](result);
if (listenerResult && typeof listenerResult.then === "function") {
listenerResult.then(deferred.resolve, deferred.reject, deferred.update);
continue;
}
deferred.resolve(listenerResult);
} catch (e) {
deferred.reject(e);
}
} else {
deferred[state](result);
}
}
head = tail = null;
}
this.resolve = function(value) {
if (state) {
throw new Error("'" + state + "' already called");
}
state = "resolve";
result = value;
queueNotification(notify);
};
this.reject = function(error) {
if (state) {
throw new Error("'" + state + "' already called");
}
state = "reject";
result = error;
queueNotification(notify);
};
this.update = this.progress = this.notify = function(progress) {
var listener = head;
while (listener) {
if (listener.update) {
try {
listener.update(progress);
} catch (e) {}
}
listener = listener.next;
}
};
this.then = function(onResolve, onReject, onUpdate) {
var listener = {
resolve: onResolve,
reject: onReject,
update: onUpdate,
deferred: new Deferred()
};
if (head) {
tail.next = listener;
} else {
head = listener;
}
tail = listener;
if (state) {
queueNotification(notify);
}
return listener.deferred.promise;
};
this.promise = {
then: this.then
};
this.state = function() {
return state || "wait";
};
this.all = function(promises, optOnError) {
var count = promises.length, result = [], deferred = this;
function onResolve(i, value) {
if (!state) {
result[i] = value;
if (--count === 0) {
deferred.resolve(result);
}
}
}
function onReject(i, error) {
if (!state) {
if (optOnError) {
try {
onResolve(i, optOnError(error));
return;
} catch (e) {
error = e;
}
}
deferred.reject(error);
}
}
if (count === 0) {
this.resolve(result);
} else {
promises.forEach(function(promise, i) {
promise.then(onResolve.bind(null, i), onReject.bind(null, i));
});
}
return this.promise;
};
}
Deferred.when = function(value, onResolve, onReject, onUpdate) {
var promise, deferred;
if (value && typeof value.then === "function") {
promise = value;
} else {
deferred = new Deferred();
deferred.resolve(value);
promise = deferred.promise;
}
return promise.then(onResolve, onReject, onUpdate);
};
return Deferred;
});