从...开始:
不要承诺你不能交付的东西
你可能会假设 Javascript 中的 promise 是基于这个原则,确保总会有一个(预期的?)结果。
以这段代码为例:
var promesa = new Promise(
function(resolve, reject) {
alert('Hola mundo!');
}
);
promesa.then(
function(value) {
alert('Hola universo!');
}).then(
function(value) {
alert('Hola multiuniverso!');
});
在show 之前触发了一个串行alert('Hola multiuniverso!');
过程,这也可以通过嵌套的Ajax Requests来完成,这使我认为与简单的 Ajax 调用相比还有其他优点。
主要问题是(我知道有几个问题,而且在 SOes 中并没有那么清楚,但是通过对什么是承诺的解释,其中几个问题得到了解决):
- 什么是承诺?
- 插件、标准还是设计模式
Promise
? - ajax 是一种承诺类型吗?
- Promise 可以是同步和异步调用吗?
作为介绍,我将告诉您,Promise 远远超出 AJAX 请求。除了它们共享的异步概念外,它们没有直接关系。
什么是承诺?
Mozilla 给了我们一个相当清晰简洁的概念:
顾名思义,promise 只是一个对象,它可能会或可能不会在当前和未来的时间线中返回某些值。我喜欢将承诺描述为一种业力:
这同样适用于 Promise,您执行异步代码并获得您将获得响应的 Promise,它可以是现在或将来。
承诺的生命周期
Promises的历史可以追溯到Promises/A+规范,该规范详细说明了任何编译器实现应如何实现它们。
Promise会记住它执行的上下文,也就是说,它准确地知道在什么时候解析值或抛出错误。当一个 Promise 被执行时,它有 2 个状态,一个初始状态和一个最终状态:
最初,promise 具有待处理
resolve
状态,直到promise 使用解决值或错误(reject
)发生之前,它将具有该状态。当一个 Promise 达到这两种状态之一时,它不能再转换到另一种状态。从广义上讲,Promises ES6 以几乎相同的方式实现 Promises/A+ 规范。但是,与 Promises/A+ 规范不同,其中没有提到捕获错误的特殊方法,ECMAScript 实现添加了一个方法,该方法
catch
将解决代码执行期间发生的任何错误。它是插件、标准还是设计模式?
它不是设计模式
它就像一种建筑。Promises 的概念与并发编程有关。Promises、Delay、Future(在 Java 中众所周知)和 Deferred,它们都充当了我们最初不知道的结果的代理。
它不是插件
Promise 是规范,不是插件;虽然有几个插件可以使用;最著名的是
Q
.是的,这是一个标准
事实上,Promises 规范可以追溯到比 ES2015 更远的地方。Promises 是一种被称为Promses/A+的规范,它在 ECMAScript 2015/6 中实现,因此该规范属于目前在大多数现代浏览器中实现 98% 的 ECMAScript 标准(Safari Tech Preview 是唯一实现 ES6 100% 的浏览器) .
您可以在委员会存储库中查看当前提案。
ajax 是一种承诺类型吗?
它不是承诺类型,而是规范。AJAX,来自Asynchronous JavaScript And XML的首字母缩写,是一种用于发出异步请求的技术;其主要作用是在不确定的时间内获得响应,但不影响主进程,从而避免重新加载当前文档。AJAX 的历史可以追溯到 1990 年代后期,在 Microsoft Outlook 开发期间。
AJAX 的工作原理
XMLHttpRequest
您可以在W3 living draft中查看规范。正如我在介绍中所说,AJAX 与 Promises 没有直接关系,因为它们没有实现它,至少没有实现
XMLHttpRequest
。另一方面,jQuery 实现了自己的延迟系统,该系统在. 但是,最近出现了一个用于 AJAX 的新 API:它确实使用原生 Promises。$.ajax
$.deferred
fetch
Promise 可以是同步和异步调用吗?
不,promise总是异步的,没有办法让它同步。可以做的是让它看起来好像是同步的,这要归功于
Async/Await
它刚刚进入第 4 阶段并将包含在今年的 ECMAScript 版本中。使用这个新规范,我们可以在异步的同时等待响应,就好像它是一个标准的同步函数一样。示例(需要 Chromium 55+ 才能运行)
正如你所看到的,乍一看,我们似乎是同步/顺序地执行承诺,但事实并非如此,幕后发生了一些神奇的事情。当我们使用时
await
,我们所做的就是等待该承诺的响应。从技术上讲,代码的执行(不阻塞)会被暂停,直到 promise 被解决(完成或拒绝)。一旦 promise 被解决,代码的执行将继续。如果您需要详细了解 VM 和 EventLoop 的工作原理,我强烈建议您观看去年 JSFOO 会议上的这段视频。
我应该使用 Promise 吗?
我的回答是肯定的。尽可能使用它们,它在语义上得到了改进,它也是一种认知上的改进(如果你见过回调地狱,它会让你大吃一惊),而且,与异步函数(
async/await
)一起,你可以拥有一个干净,整洁,最重要的是异步代码; Python 用 asyncio 实现了什么!但是,您不应该丢弃回调,因为它们是函数式语言的基础部分。Promise 不是用来代替回调,而是作为语言的补充。一般而言,由于回调具有和缺少的特殊性(例如链接),使用 Promise 为我们提供了对异步代码的更好控制。
一些优点:
async/await
资料来源:
了解承诺及其重要性
有一些基本的东西能够理解它们所暗示的承诺和革命。JavaScript 是单线程的,这意味着脚本的两个部分不能同时执行,它们必须一个接一个地执行。在浏览器中,JavaScript 与大量其他内容共享一个线程,这些内容因浏览器而异。但是 JavaScript 通常与绘画、更新样式和控制用户操作(例如突出显示文本和与表单控件交互)在同一个队列中。其中一种元素的活动会阻碍其他元素的活动。
为了避免这种情况,到目前为止,已经使用了事件和回调。
例如:
不幸的是,在上面的示例中,事件可能在我们开始听到它们之前发生。因此,我们必须使用图像的“完整”属性来解决这个问题:
这不会捕获在我们听到之前产生错误的图像。不幸的是,DOM 没有给我们提供方法来做到这一点。此外,在此示例中,我们仅尝试加载图像。当我们想知道一组图像何时加载时,复杂性会进一步增加。
事件对于在同一个对象上可能发生多次的事情非常有用,因为在附加监听器之前你并不真的想知道发生了什么。但如果它是异步成功/失败,理想情况下,你会想要这样的东西:
Promise 可以做到这一点,尽管有更好的命名法。如果 HTML 图像元素有一个返回承诺的“就绪”方法,我们可以执行以下操作:
从根本上说,promise 有点像事件监听器,除了以下几点:
这对于异步过程的成功或失败非常有用,因为可用性的确切时刻不如对结果的反应重要。
那么......什么是承诺?
它们是一个 API,可以帮助我们完成之前由于上述内容而变得复杂或不可能的事情。
该图向我们展示了一个 promise 的生命周期和操作。正如承诺的那样,我已经翻译了图像的解释:)
一个promise可以是这些类:
fulfilled
(fulfilled):与 promise 相关的动作成功完成。rejected
(rejected):与promise相关的action没有成功完成。pending
(待定)- 尚未完成或被拒绝。settled
(已完成)– 完成或拒绝。在规范中,该术语似乎
thenable
也描述了一个类似 Promise 的对象,因为它有一个方法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 ...
......这是一件很棒的事情!多亏了 Promise,开发人员将能够避免回调地狱,并且可以像传递任何其他变量一样传递异步交互。可能需要一些时间来习惯使用它们,但这些工具已经在手边,因为它们是大多数现代浏览器的原生工具。现在是学习如何使用它们的时候了!
链接