Если вы хотите понять, почему метод .promise()
стал доступен, и почему обещание не было просто возвращено просто так, обратите внимание на то, как такой API развивается с течением времени, и необходимо поддерживать обратную совместимость.
Давайте построим что-то похожее, но значительно упрощенное.Давайте сделаем функцию, которая обеспечивает 1 / x для данного числа, но объект ошибки, когда x = 0.Это будет сделано асинхронно.
Кроме того, мы хотим, чтобы функция возвращала синхронно объект, который позволяет регистрировать прослушиватель для случая возникновения ошибки, другой - для случая успеха, иеще один, когда происходит одно из двух.Это очень упрощенная идея из того, что возвращает AWS: там вы получаете очень богатый Request
объект.
Так что представьте, что мы находимся в 2012 году, и обещания еще не широко доступны / не используются вJavaScript.Поэтому мы предоставляем следующий API для нашей асинхронной функции 1 / x:
// Implementation in the first version of the API (without promises):
// * returns an object with which you can register a listener
// * accepts an optional callback
function getAsyncInverse(num, callback) {
var onSuccess = [], // callbacks that are called on success
onError = [], // callbacks that are called on failure
onComplete = []; // callbacks that are called in both situations
if (callback) onComplete.push(callback);
function complete(err=null) {
var result = null;
if (num === 0) err = new Error("Division by Zero");
else result = 1/num;
// Communicate the result/error to the appropriate listeners:
if (err) for (var i = 0; i < onError.length; i++) onError[i](err);
else for (var i = 0; i < onSuccess.length; i++) onSuccess[i](result);
for (var i = 0; i < onComplete.length; i++) onComplete[i](err, result);
}
var timeoutId = setTimeout(complete, 100);
var request = {
on: function (type, callback) {
if (type === "success") onSuccess.push(callback);
else if (type === "error") onError.push(callback);
else if (type === "complete") onComplete.push(callback);
return request;
},
abort: function () {
clearTimeout(timeoutId);
complete(new Error("aborted"));
return request;
}
}
return request;
}
// How version 1 is used, by registering a listener via the returned object
var num = 2;
var request = getAsyncInverse(num); // let's not pass a callback here
request.on("success", function (result) { // ... but use the request object
console.log("The result is:", result);
}).on("error", function (err) {
console.log("There was an error:", err);
});
Но тогда обещания становятся более популярными, и пользователи вашего API стремятся к API обещаний.Вы хотите обеспечить обратную совместимость и поэтому решили просто расширить возвращаемый объект запроса одним дополнительным свойством, методом: promise()
Вот как будет изменена вышеупомянутая реализация, чтобы это произошло:
// Implementation in the second version of the API (with promise), but backwards-compatible
// * returns an object with which you can register a listener, or get the promise object
// * accepts an optional callback
function getAsyncInverse(num, callback) {
let onSuccess = [], // callbacks that are called on success
onError = [], // callbacks that are called on failure
onComplete = []; // callbacks that are called in both situations
if (callback) onComplete.push(callback);
let request;
// New: create a promise, and put the logic inside the promise-constructor callback
let promise = new Promise(function (resolve, reject) {
function complete(err=null) {
let result = null;
if (num === 0) err = new Error("Division by Zero");
else result = 1/num;
// Communicate the result/error to the appropriate listeners:
if (err) for (let callback of onError) callback(err);
else for (let callback of onSuccess) callback(result);
for (let callback of onComplete) callback(err, result);
// New: also call resolve/reject
if (err) reject(err);
else resolve(result);
}
let timeoutId = setTimeout(complete, 100);
request = {
on: function (type, callback) {
if (type === "success") onSuccess.push(callback);
else if (type === "error") onError.push(callback);
else if (type === "complete") onComplete.push(callback);
return request;
},
abort: function () {
clearTimeout(timeoutId);
complete(new Error("aborted"));
return request;
},
promise: function () { // <--- added feature!
return promise;
}
};
});
return request; // We return the same as in version-1, but with additional promise method
}
// How version 2 is used, by getting the new promise method
let num = 2;
let promise = getAsyncInverse(num).promise();
promise.then(function (result) {
console.log("The result is:", result);
}).catch(function (err) {
console.log("There was an error:", err);
});
Как видите, было бы не очень хорошей идеей опускать объект запроса, и функция возвращала обещание.Это сломало бы существующий код, используя ваш API (обратной совместимости нет).