Ich finde es schwieriger zu erklären, als ein Beispiel zu zeigen. Hier ist eine sehr einfache Implementierung dessen, was ein Aufschub / Versprechen sein könnte.
Haftungsausschluss: Dies ist keine funktionale Implementierung und einige Teile der Promise / A-Spezifikation fehlen. Dies dient nur zur Erläuterung der Grundlage der Versprechen.
tl; dr: Gehen Sie zum Abschnitt Klassen und Beispiel erstellen , um die vollständige Implementierung zu sehen.
Versprechen:
Zuerst müssen wir ein Versprechungsobjekt mit einer Reihe von Rückrufen erstellen. Ich werde mit Objekten arbeiten, weil es klarer ist:
var promise = {
callbacks: []
}
Fügen Sie jetzt Rückrufe mit der Methode hinzu und dann:
var promise = {
callbacks: [],
then: function (callback) {
callbacks.push(callback);
}
}
Und wir brauchen auch die Fehlerrückrufe:
var promise = {
okCallbacks: [],
koCallbacks: [],
then: function (okCallback, koCallback) {
okCallbacks.push(okCallback);
if (koCallback) {
koCallbacks.push(koCallback);
}
}
}
Verschieben:
Erstellen Sie nun das verzögerte Objekt, das ein Versprechen hat:
var defer = {
promise: promise
};
Der Aufschub muss behoben werden:
var defer = {
promise: promise,
resolve: function (data) {
this.promise.okCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(data)
}, 0);
});
},
};
Und muss ablehnen:
var defer = {
promise: promise,
resolve: function (data) {
this.promise.okCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(data)
}, 0);
});
},
reject: function (error) {
this.promise.koCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(error)
}, 0);
});
}
};
Beachten Sie, dass die Rückrufe in einem Timeout aufgerufen werden, damit der Code immer asynchron ist.
Und das ist es, was eine grundlegende Defer / Promise-Implementierung benötigt.
Klassen und Beispiel erstellen:
Lassen Sie uns nun beide Objekte in Klassen konvertieren, zunächst das Versprechen:
var Promise = function () {
this.okCallbacks = [];
this.koCallbacks = [];
};
Promise.prototype = {
okCallbacks: null,
koCallbacks: null,
then: function (okCallback, koCallback) {
okCallbacks.push(okCallback);
if (koCallback) {
koCallbacks.push(koCallback);
}
}
};
Und jetzt der Aufschub:
var Defer = function () {
this.promise = new Promise();
};
Defer.prototype = {
promise: null,
resolve: function (data) {
this.promise.okCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(data)
}, 0);
});
},
reject: function (error) {
this.promise.koCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(error)
}, 0);
});
}
};
Und hier ist ein Anwendungsbeispiel:
function test() {
var defer = new Defer();
serverCall(function (request) {
if (request.status === 200) {
defer.resolve(request.responseText);
} else {
defer.reject(new Error("Status code was " + request.status));
}
});
return defer.promise;
}
test().then(function (text) {
alert(text);
}, function (error) {
alert(error.message);
});
Wie Sie sehen können, sind die grundlegenden Teile einfach und klein. Es wächst, wenn Sie andere Optionen hinzufügen, z. B. die Auflösung mehrerer Versprechen:
Defer.all(promiseA, promiseB, promiseC).then()
oder Versprechen verketten:
getUserById(id).then(getFilesByUser).then(deleteFile).then(promptResult);
Weitere Informationen zu den Spezifikationen: CommonJS Promise Specification . Beachten Sie, dass die Hauptbibliotheken (Q, when.js, rsvp.js, Node-Promise, ...) der Promises / A- Spezifikation entsprechen.
Hoffe ich war klar genug.
Bearbeiten:
Wie in den Kommentaren gefragt, habe ich in dieser Version zwei Dinge hinzugefügt:
- Die Möglichkeit, dann ein Versprechen einzuholen, egal welchen Status es hat.
- Die Möglichkeit, Versprechen zu verketten.
Um das Versprechen bei Auflösung aufrufen zu können, müssen Sie den Status zum Versprechen hinzufügen. Wenn das Versprechen aufgerufen wird, überprüfen Sie diesen Status. Wenn der Status aufgelöst oder abgelehnt wird, führen Sie einfach den Rückruf mit seinen Daten oder Fehlern aus.
Um Versprechen verketten zu können, müssen Sie für jeden Anruf einen neuen Aufschub generieren then
und, wenn das Versprechen aufgelöst / abgelehnt wird, das neue Versprechen mit dem Ergebnis des Rückrufs auflösen / ablehnen. Wenn das Versprechen erfüllt ist und der Rückruf ein neues Versprechen zurückgibt, ist er an das mit dem zurückgegebene Versprechen gebunden then()
. Wenn nicht, wird das Versprechen mit dem Ergebnis des Rückrufs gelöst.
Hier ist das Versprechen:
var Promise = function () {
this.okCallbacks = [];
this.koCallbacks = [];
};
Promise.prototype = {
okCallbacks: null,
koCallbacks: null,
status: 'pending',
error: null,
then: function (okCallback, koCallback) {
var defer = new Defer();
this.okCallbacks.push({
func: okCallback,
defer: defer
});
if (koCallback) {
this.koCallbacks.push({
func: koCallback,
defer: defer
});
}
if (this.status === 'resolved') {
this.executeCallback({
func: okCallback,
defer: defer
}, this.data)
} else if(this.status === 'rejected') {
this.executeCallback({
func: koCallback,
defer: defer
}, this.error)
}
return defer.promise;
},
executeCallback: function (callbackData, result) {
window.setTimeout(function () {
var res = callbackData.func(result);
if (res instanceof Promise) {
callbackData.defer.bind(res);
} else {
callbackData.defer.resolve(res);
}
}, 0);
}
};
Und der Aufschub:
var Defer = function () {
this.promise = new Promise();
};
Defer.prototype = {
promise: null,
resolve: function (data) {
var promise = this.promise;
promise.data = data;
promise.status = 'resolved';
promise.okCallbacks.forEach(function(callbackData) {
promise.executeCallback(callbackData, data);
});
},
reject: function (error) {
var promise = this.promise;
promise.error = error;
promise.status = 'rejected';
promise.koCallbacks.forEach(function(callbackData) {
promise.executeCallback(callbackData, error);
});
},
bind: function (promise) {
var that = this;
promise.then(function (res) {
that.resolve(res);
}, function (err) {
that.reject(err);
})
}
};
Wie Sie sehen können, ist es ziemlich gewachsen.