2016-02-13 2 views
5

Я думаю, что мне, наконец, удалось согнуть мой разум вокруг javascript/ES6 Promises, по большей части. Это было непросто! Но что-то меня озадачивает дизайном.Является ли javascript Promise API более запутанным, чем нужно?

Почему конструктор Promise выполняет обратный вызов? Учитывая, что обратный вызов вызывается сразу, не может ли вызывающий абонент просто выполнить этот код вместо этого, тем самым избегая одного ненужного уровня изгиба ума «не называй меня, я позвоню тебе»?

Вот что я думаю о прототипическом примере использования обещаний, скопированном с Jake Archibald's Javascript Promises tutorial http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest, с комментариями разделенными.

Это обещание на основе обертка для запроса XMLHttpRequest GET:

function get(url) { 
    return new Promise(function(resolve, reject) { 
    var req = new XMLHttpRequest(); 
    req.open('GET', url); 
    req.onload = function() { 
     if (req.status == 200) { 
     resolve(req.response); 
     } 
     else { 
     reject(Error(req.statusText)); 
     } 
    }; 
    req.onerror = function() { 
     reject(Error("Network Error")); 
    }; 
    req.send(); 
    }); 
} 

Для меня, приведенный выше код будет гораздо легче понять, если бы оно было переписано следующим образом, используя очень немного другого рода обещание что я воображал, имея конструктор без аргументов и разрешить/отклонить методы:

function get(url) { 
    var promise = new MyEasierToUnderstandPromise(); 
    var req = new XMLHttpRequest(); 
    req.open('GET', url); 
    req.onload = function() { 
    if (req.status == 200) { 
     promise.resolve(req.response); 
    } 
    else { 
     promise.reject(Error(req.statusText)); 
    } 
    }; 
    req.onerror = function() { 
    promise.reject(Error("Network Error")); 
    }; 
    req.send(); 
    return promise; 
} 

MyEasierToUnderstandPromise не слишком сложно реализовать с точки зрения Promise. Сначала я попытался сделать его фактическим подклассом Promise, но по какой-то причине я не мог заставить его работать; так что вместо этого я реализовал его как простую функцию фабрики, которая возвращает обычный старый объект Promise с парой дополнительных функций прилагается, которые ведут себя как функции-члены:

function NewMyEasierToUnderstandPromise() { 
    var resolveVar; 
    var rejectVar; 
    var promise = new Promise(function(resolveParam, rejectParam) { 
    resolveVar = resolveParam; 
    rejectVar = rejectParam; 
    }); 
    promise.resolve = resolveVar; 
    promise.reject = rejectVar; 
    return promise; 
}; 

Итак, почему не Обещай предназначен как это? Я думаю, что если бы это было так, это помогло бы мне понять обещания намного быстрее. Держу пари, это сократило бы время обучения пополам.

Я знаю, что многие умные люди приложили руку к созданию API обещаний, и все, кажется, в целом счастливы и гордятся этим, поэтому мне интересно, о чем они думали.

+1

ваших «easierToUnderstandPromise» подобен jQuery.Deferred в пути. С вашим дизайном возвращаемое обещание обязательно предоставляет методы разрешения/отклонения. Я где-то читал, почему это «плохо», но я не могу найти этот ресурс (прошло много лет с тех пор, как я его прочитал) –

+0

Подробнее о [ES7 async/await] (https://jakearchibald.com/2014/ES7 Асинхр-функции /). –

+0

Существует также отложенный шаблон, но [он устарел по уважительной причине] (http://stackoverflow.com/q/28687566/1048572) – Bergi

ответ

6

Ваша версия не является исключением, в то время как Promises/A + являются безопасными, так как они пойманы конструктором Promise.

+0

Отличный ответ, спасибо. Я думаю, что все вступительные документы и учебные пособия были бы значительно улучшены, упомянув об этом: мой мозг просто не хочет поглощать вещи, для которых он не может видеть причину! –

1

Как вспомогательной информация, когда сценарий определяет ES6 обещание церь

var promise = new Promise(executor).then(something).catch(handleError); 

переменного обещания остался установить на обещание, возвращенное при вызове метода .catch. Целые цепочки или объекты Promise фактически предотвращаются от сбора мусора с помощью ссылок на функцию разрешения/отклонения, выполняемых функцией-исполнителем. Если вызывается разрешение (обычно асинхронно после того, как исполнитель возвращает, но до того, как разрешить функции разрешения/отклонения выходят из области видимости), вызывается then прослушиватель «что-то», с внутренним исполнителем платформы Promise, содержащим ссылки на функцию разрешения/отклонения для обещания, возвращенного вызов then, чтобы предотвратить его и любые последующие прикованные обещания от самих себя, прежде чем сбор мусора.

В соответствии с предложенной моделью вы не можете установить цепочку обещаний таким образом, потому что вам нужна ссылка на , а именно: обещание в цепочке, чтобы разрешить или отклонить его.Код становится больше похож

var promise = new Promise(); // no executor 
promise.then(something).catch(handleError); 
initiateOperation(promise); 

, а затем асинхронно в операции кодом_ответа

promise.resolve(value); // if no error occurred 
promise = null;   // explicit discard of first promise reference to allow GC? 

Общий minimilistic подход ES6 обещает в настоящее время начинает выглядеть перспективным (Уч). Я сочувствую вашим трудностям в изучении того, как работают обещания - интересное путешествие !!!

+1

Нет, вам не нужно явно отбрасывать ссылки на «обещание» в обратном вызове async, так же, как вам не нужно явно отбрасывать ссылки на 'resolve' /' reject' в области исполнителей. – Bergi

5

Обещания предназначены для использования в качестве значений. Подход конструктора ES инкапсулирует создание Promise, которое затем может быть передано как значение. Когда это значение передается, потребитель этого значения не нуждается в resolve и reject, и поэтому эти функции не должны быть частью общедоступного API.

(и все, что материал об обработке исключений и цепочке)

Смежные вопросы