Начиная с:
Не обещай того, что не сможешь выполнить
Вы можете предположить, что обещание в Javascript основано на этом принципе, гарантируя, что всегда будет ( ожидаемый? ) результат.
Взяв этот код в качестве примера:
var promesa = new Promise(
function(resolve, reject) {
alert('Hola mundo!');
}
);
promesa.then(
function(value) {
alert('Hola universo!');
}).then(
function(value) {
alert('Hola multiuniverso!');
});
Последовательный процесс запускается до показа alert('Hola multiuniverso!');
, что также может быть выполнено с вложенными запросами Ajax , что заставляет меня предположить, что есть и другие преимущества по сравнению с простым вызовом Ajax.
Основными вопросами будут (я знаю, что их несколько, и это не так хорошо видно внутри SO, но с объяснением того, что такое промис, некоторые из них решаются):
- Что такое обещание?
Promise
плагин , стандарт или шаблон проектирования ?- Является ли ajax типом обещания?
- Могут ли обещания быть синхронными и асинхронными вызовами?
В качестве введения я скажу вам, что обещания выходят далеко за рамки запросов AJAX. Они не связаны напрямую, за исключением общей концепции асинхронности .
Что такое обещание?
Mozilla дает нам довольно четкую и лаконичную концепцию:
Промис, как следует из его названия, — это просто объект, который может возвращать или не возвращать какое-то значение на временной шкале настоящего и будущего. Мне нравится описывать обещание как своего рода карму:
То же самое относится и к обещаниям, вы выполняете асинхронный код и получаете обещание, что получите ответ, который может быть сейчас или в будущем.
Жизненный цикл обещания
История промисов восходит к спецификации Promises/A+ , в которой подробно описывается, как их должна реализовывать любая реализация компилятора .
Обещание запоминает контекст, в котором оно выполняется, то есть точно знает, в какой момент значение должно быть разрешено или выдается ошибка. Когда обещание выполняется, оно имеет 2 состояния, начальное и конечное:
Первоначально промис находится в состоянии ожидания , в котором он будет находиться до тех пор, пока промис не разрешит значение using
resolve
или не произойдет ошибка (reject
). Когда обещание достигает одного из этих двух состояний, оно больше не может перейти в другое.Вообще говоря, Promises ES6 реализует спецификацию Promises/A+ почти таким же образом. Однако, в отличие от спецификации Promises/A+, где нет упоминания о специальном методе отлова ошибок, в реализации ECMAScript добавлен метод
catch
, устраняющий любые ошибки, возникшие во время выполнения кода.Это плагин, стандарт или шаблон проектирования?
Это не шаблон проектирования
Это было бы своего рода архитектурой . Концепция промисов связана с параллельным программированием. Оба Обещания, Задержка, Будущее (хорошо известные в Java) и Отложенные, они действуют как прокси для результата, который мы изначально не знаем.
Это не плагин
Promise — это спецификация, а не плагин; хотя есть несколько плагинов для использования; самый известный из них
Q
.да это стандарт
На самом деле спецификация Promises уходит корнями дальше ES2015. Promises — это спецификация, известная как Promses/A+ , которая была реализована в ECMAScript 2015/6, поэтому эта спецификация относится к стандарту ECMAScript, который в настоящее время на 98% реализован в большинстве современных браузеров (Safari Tech Preview — единственный браузер, реализующий ES6 на 100%). .
С текущими предложениями можно ознакомиться в репозитории комитета .
Является ли ajax типом обещания?
Это не тип обещания, это спецификация. AJAX, от аббревиатуры Asynchronous JavaScript And XML , представляет собой метод выполнения асинхронных запросов; основной эффект которого заключается в получении ответа в неопределенное время, но без влияния на основной процесс, что позволяет избежать перезагрузки текущего документа. История AJAX восходит к концу 1990-х годов, во время разработки Microsoft Outlook.
Как работает АЯКС
Вы можете увидеть спецификацию
XMLHttpRequest
в живом черновике W3 .Как я сказал во введении, AJAX не имеет ничего общего с промисами, потому что они его не реализуют, по крайней мере
XMLHttpRequest
. С другой стороны, jQuery реализует собственную отложенную систему, которая используется$.ajax
в$.deferred
. Однако недавно появился новый API для AJAX:fetch
он изначально использует промисы.Могут ли обещания быть синхронными и асинхронными вызовами?
Нет. Промис всегда будет асинхронным , и сделать его синхронным невозможно. Что можно сделать, так это сделать так, чтобы он выглядел синхронно, и это благодаря тому факту
Async/Await
, что он только что достиг стадии 4 и будет включен в версию ECMAScript этого года. Используя эту новую спецификацию, мы можем ожидать ответа, оставаясь при этом асинхронными, как если бы это была стандартная синхронная функция.Пример (для запуска требуется Chromium 55+)
Как видите, на первый взгляд кажется, что мы выполняем промис синхронно/последовательно, но это не так и за кадром происходит что-то волшебное . Когда мы используем
await
то, что мы делаем, мы ждем ответа на это обещание. Технически выполнение кода без блокировки приостанавливается до тех пор, пока обещание не будет разрешено (выполнено или отклонено).Как только обещание будет разрешено, выполнение кода продолжится. Если вам нужно подробно разобраться, как работает виртуальная машина и EventLoop, очень рекомендую посмотреть это видео с прошлогодней конференции JSFOO.
Должен ли я использовать промисы?
Мой ответ абсолютно да . Используйте их всякий раз, когда можете, это улучшает семантику, а также когнитивное улучшение (если вы видели ад обратных вызовов, это дает вам удар), кроме того, вместе с асинхронными функциями (
async/await
) вы можете получить чистый, аккуратный и, прежде всего, асинхронный код; Чего Python добился с помощью asyncio!Однако вы не должны отказываться от обратных вызовов, потому что они являются фундаментальной частью функционального языка. Обещания были здесь не для замены обратных вызовов , а как дополнение к языку. В общих чертах, использование промисов дает нам больший контроль над асинхронным кодом благодаря особенностям, которые есть и отсутствуют у обратных вызовов (таких как цепочка).
Некоторые плюсы:
async/await
Источники:
Понимание обещаний и их важности
Есть что-то фундаментальное, чтобы понять обещания и революцию, которую они подразумевают. JavaScript является однопоточным, то есть две части скрипта не могут выполняться одновременно, они должны выполняться одна за другой. В браузерах JavaScript разделяет поток с множеством других вещей, которые отличаются от браузера к браузеру. Но JavaScript, как правило, находится в той же очереди, что и рисование, обновление стилей и управление действиями пользователя (такими как выделение текста и взаимодействие с элементами управления формы). Активность одного из этих элементов тормозит другие.
Чтобы избежать этого, до сих пор использовались события и обратные вызовы.
Например:
К сожалению, в приведенном выше примере возможно, что события происходят до того, как мы начинаем их слышать. Следовательно, мы должны решить эту проблему, используя свойство «полных» изображений:
Это не захватывает изображения, которые вызвали ошибку до того, как мы их услышали. К сожалению, DOM не дает нам возможности сделать это. Кроме того, в этом примере мы пытаемся загрузить только изображение. Сложность возрастает еще больше, когда мы хотим знать, когда был загружен набор изображений.
События отлично подходят для вещей, которые могут происходить несколько раз с одним и тем же объектом, потому что тогда вы действительно не хотите знать, что произошло до того, как вы подключили прослушиватель. Но если это асинхронный успех/неудача, в идеале вам нужно что-то вроде этого:
Обещания делают это, хотя и с лучшей номенклатурой. Если бы у HTML-элементов изображения был «готовый» метод, возвращающий обещание, мы могли бы сделать следующее:
По сути, промисы немного похожи на прослушиватели событий, за исключением следующего:
Это чрезвычайно полезно для успеха или неудачи асинхронных процессов, потому что точный момент доступности менее важен, чем реакция на результат.
Итак... что такое обещания?
Это API, который поможет нам делать вещи, которые ранее были сложными или невозможными из-за того, что было сказано выше.
Изображение показывает нам жизненный цикл и работу промиса. Как и обещал , я перевел пояснения к изображению :)
Обещание может быть следующих классов:
fulfilled
(выполнено): действие, связанное с обещанием, успешно завершено.rejected
(отклонено): действие, связанное с обещанием, не выполнено успешно.pending
(в ожидании) - еще не завершено или отклонено.settled
(Завершено) — завершает или отклоняет.В спецификациях этот термин также появляется
thenable
для описания объекта, похожего на промис, поскольку у него есть методthen
.Cabe decir que hace tiempo que las promesas existen en forma de bibliotecas. Las siguientes son algunas:
La revolución de las promesas empezó a través de librerías como estas y de frameworks que las usan como medio primario para manipular la asincronía en su código. Desde el 2013, las promesas están disponibles de manera nativa en los exploradores modernos, lo cual será decisivo en el futuro.
Estas bibliotecas y las promesas de JavaScript tienen en común un comportamiento estandarizado llamado Promises/A+. Si usas jQuery, encontrarás algo similar llamado Deferred. Sin embargo, Deferred no cumple con Promise/A+, por lo cual es un tanto diferente y menos útil, así que ten cuidado. jQuery también tiene un tipo Promise, pero solo se trata de un subconjunto de Deferred y no funciona muy bien.
Si bien las implementaciones de las promesas cumplen con un comportamiento estandarizado, las API generales son diferentes. Las API de las promesas de JavaScript son similares a las de RSVP.js.
Las promesas de JavaScript empezaron en DOM como “Future”, se les cambió el nombre a “Promise” y, finalmente, se trasladaron a JavaScript. Es fabuloso contar con ellas en lugar del DOM en JavaScript porque estarán disponibles en contextos de JS sin navegador, como Node.js.
Si bien son una funcionalidad de JavaScript, el DOM las usa sin problemas cuando las necesita. De hecho, todas las nuevas API de DOM con métodos de éxito o falla asincrónicos usan promesas.
Viendo una promesa por dentro
Una promesa se crea así:
El constructor de la promesa recibe un argumento: un callback con dos parámetros (resolve y reject). A continuación, se hace algo con el callback (tal vez un proceso asincrónico) y se llama a resolve si todo funciona bien o a reject si esto no sucede.
Como en throw del JavaScript que todos conocemos, es costumbre (aunque no obligación) aplicar reject con un objeto Error. La ventaja de los objetos Error es que capturan un seguimiento de pila; de esta forma, las herramientas de depuración son más útiles.
Para usar esta promesa:
then()
recibe dos argumentos: un callback para cuando se tiene éxito y otro para cuando sucede lo contrario. Ambos son opcionales; puedes agregar un callback solo para cuando se tiene éxito o se produce una falla.Uso básico de las Promesas
Pienso esta parte como una especie de Promise by the example, para mostrar algunos casos y ejemplos de uso de las promesas.
El constructor
new Promise()
sólo debe utilizarse para tareas asíncronas heredadas, como el uso desetTimeout
oXMLHttpRequest
. Se crea una nueva promesa con la nueva palabra clave y la promesa proporciona funciones de resolución y rechazo a la devolución de llamada proporcionada:Corresponde al desarrollador llamar manualmente a
resolve
oreject
dentro del cuerpo de la devolución de llamada en función del resultado de su tarea. Un ejemplo realista sería convertirXMLHttpRequest
a una tarea basada en la promesa:A veces no es necesario completar tareas asíncronas dentro de la promesa, si es posible que se tome una acción asíncrona, sin embargo, devolver una promesa será lo mejor para que siempre pueda contar con una promesa que sale de una función dada . En ese caso, simplemente puede llamar a
promise.resolve()
opromise.reject()
sin usar la nueva palabra clave. Por ejemplo:Puesto que siempre se devuelve una promesa, siempre puede usar los métodos
then
ycatch
en su valor de retorno.then
Todas las instancias de Promise tienen un método
then
que nos permite reaccionar a la promesa. El primer método de devolución de llamada recibe el resultado dado por la llamadaresolve()
:La llamada de retorno se activa cuando se resuelve la promesa. También puede encadenar las devoluciones de llamada del método:
Cada
then
recibe el resultado del valor de retorno anterior.Si una promesa ya se ha resuelto pero se vuelve a llamar, la devolución de llamada se disparará inmediatamente. Si la promesa es rechazada y usted llama entonces después del rechazo, el callback nunca se llama.
El callback
catch
se ejecuta cuando se rechaza la promesa:Lo que usted proporcione al método de rechazo depende de usted. Un patrón frecuente es enviar un error a la captura:
promise.all
Piense en los cargadores de JavaScript: hay momentos en los que se desencadenan múltiples interacciones asíncronas, pero sólo se quiere responder cuando se completan todos ellos - ahí es donde
promise.all
entra en juego. El métodopromise.all
toma una serie de promesas y dispara una devolución de llamada una vez todos están resueltos:Una forma perfecta de pensar en
Promise.all
es disparar múltiples solicitudes AJAX (via fetch) al mismo tiempo:Podrías combinar APIs como
fetch
yBattery API
, ya que ambas retornan promesa:Lidiar con el rechazo es, por supuesto, difícil. Si alguna promesa es rechazada el
catch
es lanzado en el primer rechazo:Promise.all
será super útil a medida que más APIs se mueven hacia promesas.Promise.race
Un caso de uso podría estar provocando una solicitud a una fuente primaria y una fuente secundaria (en caso de que la primaria o la secundaria no estén disponibles).
Compatibilidad con navegadores y polyfill
En la actualidad, ya existen implementaciones de promesas en los navegadores.
A partir de Chrome 32, Opera 19, Firefox 29, Safari 8 y Microsoft Edge, las promesas vienen habilitadas de forma predeterminada.
Consulta el polyfill si deseas que los navegadores sin implementaciones completas de promesas cumplan con las especificaciones, o si quieres agregar promesas a otros navegadores y Node.js.
Compatibilidad con otras bibliotecas
La API de las promesas de JavaScript tratará a todos los elementos con un método
then()
como si fueran promesas (o thenable, si se usa el idioma de las promesas). Por lo tanto, no habrá problema si usas una biblioteca que muestra promesas; funcionará bien con las nuevas promesas de JavaScript.A pesar de que, como ha dicho, los Deferreds de jQuery son un poco inútiles. Afortunadamente, puedes transmitirlos a las promesas convencionales. Vale la pena hacerlo lo más pronto posible.
Ejemplo:
En este caso,
$.ajax
de jQuery muestra un elemento Deferred. Ya que tiene un métodothen(), Promise.resolve()
puede convertirlo en una promesa de JavaScript. Sin embargo, algunos deferreds pasan varios argumentos a sus callbacks, por ejemplo:En cambio, las promesas de JS ignoran todos menos el primero:
Afortunadamente, esto suele ser lo que quieres o, al menos, te brinda acceso a lo que quieres. Además, ten en cuenta que jQuery no sigue la convención de pasar objetos Error a rechazos.
Conclusión
Las promesas han sido un tema candente para los últimos años, y han pasado de un patrón de framework de JavaScript a un elemento básico del idioma. Iremos viendo cómo la mayoría de las nuevas API JavaScript se implementarán con un patrón basado en la promesa ...
...и это здорово! Благодаря обещаниям разработчики смогут избежать ада обратных вызовов, а асинхронные взаимодействия можно будет передавать, как и любую другую переменную. Может потребоваться некоторое время, чтобы привыкнуть к ним, но инструменты уже под рукой, так как они встроены в большинство современных браузеров. Сейчас самое время научиться ими пользоваться!
Ссылки