¿Qué significa la función then () en JavaScript?

275

He estado viendo código que se parece a:

myObj.doSome("task").then(function(env) {
    // logic
});

De donde then()viene

Kay pálido
fuente
8
ACTUALIZADO: descubrí que tiene que ver con las promesas de CommonJS
Kay Pale

Respuestas:

349

La forma tradicional de lidiar con llamadas asincrónicas en JavaScript ha sido con devoluciones de llamada. Digamos que tuvimos que hacer tres llamadas al servidor, una tras otra, para configurar nuestra aplicación. Con las devoluciones de llamada, el código podría tener un aspecto similar al siguiente (suponiendo una función xhrGET para hacer que el servidor llame):

// Fetch some server configuration
    xhrGET('/api/server-config', function(config) {
        // Fetch the user information, if he's logged in
        xhrGET('/api/' + config.USER_END_POINT, function(user) {
            // Fetch the items for the user
            xhrGET('/api/' + user.id + '/items', function(items) {
                // Actually display the items here
            });
        });
    });

En este ejemplo, primero buscamos la configuración del servidor. Luego, en base a eso, buscamos información sobre el usuario actual y luego finalmente obtenemos la lista de elementos para el usuario actual. Cada llamada xhrGET toma una función de devolución de llamada que se ejecuta cuando el servidor responde.

Ahora, por supuesto, cuantos más niveles de anidamiento tengamos, más difícil será leer, depurar, mantener, actualizar y básicamente trabajar con el código. Esto se conoce generalmente como infierno de devolución de llamada. Además, si necesitáramos manejar errores, posiblemente debamos pasar otra función a cada llamada xhrGET para decirle qué debe hacer en caso de un error. Si quisiéramos tener un solo controlador de errores común, eso no es posible.

La API Promise fue diseñada para resolver este problema de anidamiento y el problema del manejo de errores.

Promise API propone lo siguiente:

  1. Cada tarea asincrónica devolverá un promiseobjeto.
  2. Cada promiseobjeto tendrá una thenfunción que puede tomar dos argumentos, un success controlador y un errorcontrolador.
  3. El controlador de éxito o error en la thenfunción se llamará solo una vez , una vez que finalice la tarea asincrónica.
  4. La thenfunción también devolverá a promise, para permitir encadenar múltiples llamadas.
  5. Cada controlador (éxito o error) puede devolver un value, que se pasará a la siguiente función como un argument, en la cadena de promises.
  6. Si un controlador devuelve un promise(realiza otra solicitud asincrónica), entonces se llamará al siguiente controlador (éxito o error) solo después de que finalice la solicitud.

Entonces, el código de ejemplo anterior podría traducirse a algo como lo siguiente, usando promesas y el $httpservicio (en AngularJs):

$http.get('/api/server-config').then(
    function(configResponse) {
        return $http.get('/api/' + configResponse.data.USER_END_POINT);
    }
).then(
    function(userResponse) {
        return $http.get('/api/' + userResponse.data.id + '/items');
    }
).then(
    function(itemResponse) {
        // Display items here
    }, 
    function(error) {
        // Common error handling
    }
);

Propagación de éxito y error

El encadenamiento de promesas es una técnica muy poderosa que nos permite lograr muchas funciones, como hacer que un servicio realice una llamada al servidor, realice un procesamiento posterior de los datos y luego devuelva los datos procesados ​​al controlador. Pero cuando trabajamos con promisecadenas, hay algunas cosas que debemos tener en cuenta.

Considere la siguiente promisecadena hipotética con tres promesas, P1, P2 y P3. Cada uno promisetiene un controlador de éxito y un controlador de errores, por lo que S1 y E1 para P1, S2 y E2 para P2, y S3 y E3 para P3:

xhrCall()
  .then(S1, E1) //P1
  .then(S2, E2) //P2
  .then(S3, E3) //P3

En el flujo normal de cosas, donde no hay errores, la aplicación fluiría a través de S1, S2 y, finalmente, S3. Pero en la vida real, las cosas nunca son tan suaves. P1 podría encontrar un error, o P2 podría encontrar un error, disparando E1 o E2.

Considere los siguientes casos:

• Recibimos una respuesta exitosa del servidor en P1, pero los datos devueltos no son correctos o no hay datos disponibles en el servidor (piense en una matriz vacía). En tal caso, para la próxima promesa P2, debería activar el controlador de errores E2.

• Recibimos un error para la promesa P2, disparando E2. Pero dentro del controlador, tenemos datos del caché, lo que garantiza que la aplicación se pueda cargar normalmente. En ese caso, podríamos asegurarnos de que después de E2, se llame a S3.

Entonces, cada vez que escribimos un manejador de éxito o error, debemos hacer una llamada, dada nuestra función actual, ¿es esta promesa un éxito o un fracaso para el próximo manejador en la cadena de la promesa?

Si queremos activar el controlador de éxito para la próxima promesa en la cadena, podemos devolver un valor del controlador de éxito o error

Si, por otro lado, queremos activar el controlador de errores para la próxima promesa en la cadena, podemos hacerlo usando un deferredobjeto y llamando a su reject()método

Ahora ¿Qué es el objeto diferido?

Los objetos diferidos en jQuery representan una unidad de trabajo que se completará más tarde, generalmente de forma asíncrona. Una vez que se completa la unidad de trabajo, el deferredobjeto se puede configurar como resuelto o fallido.

Un deferredobjeto contiene un promiseobjeto. A través del promiseobjeto puede especificar qué sucederá cuando se complete la unidad de trabajo. Para ello, establezca funciones de devolución de llamada en el promiseobjeto.

Objetos diferidos en Jquery: https://api.jquery.com/jquery.deferred/

Objetos diferidos en AngularJs: https://docs.angularjs.org/api/ng/service/ $ q

Sid
fuente
3
Muy bien escrito Esto me ha ayudado a concretar las promesas.
Ju66ernaut
¿Es el controlador de errores, el segundo parámetro, siempre opcional?
1.21 gigavatios
¡Esta es la mejor respuesta que he visto hasta ahora!
Imam Bux
78

La función then () está relacionada con las "promesas Javascript" que se usan en algunas bibliotecas o frameworks como jQuery o AngularJS.

Una promesa es un patrón para manejar operaciones asincrónicas. La promesa le permite llamar a un método llamado "entonces" que le permite especificar las funciones que se utilizarán como devoluciones de llamada.

Para obtener más información, consulte: http://wildermuth.com/2013/8/3/JavaScript_Promises

Y para las promesas angulares: http://liamkaufman.com/blog/2013/09/09/using-angularjs-promises/

almoraleslopez
fuente
44
Entonces, ¿es como una devolución de llamada que se ejecuta cuando se realiza la tarea? ¿Cómo es diferente
Muhammad Umer
3
las promesas de JavaScript en el otro comentario dice: A promise can only succeed or fail onceyIf a promise has succeeded or failed and you later add a success/failure callback, the correct callback will be called
Xiao
Además, las pepitas de Promise se explica cómo utilizar promisey qué se puede hacer concallback
Xiao
En la primera página, faltan fragmentos de código (grandes espacios en blanco). La mayoría de las personas pensarán en inspeccionar el elemento y encontrarán las URL del violín debajo. Este mensaje es para el resto: los violines aún funcionan;)
DanteTheSmith
1
@MuhammadUmer: lea este stackoverflow.com/a/31453579/1350476 (respuesta de Sid)
SharpCoder
32

Que yo sepa, no hay un then()método incorporado en javascript(en el momento de escribir este artículo).

Parece que lo que sea que doSome("task")está regresando tiene un método llamado then.

Si registra el resultado devuelto doSome()en la consola, debería poder ver las propiedades de lo que se devolvió.

console.log( myObj.doSome("task") ); // Expand the returned object in the
                                     //   console to see its properties.

ACTUALIZACIÓN (a partir de ECMAScript6) : -

La .then()función se ha incluido en javascript puro.

De la documentación de Mozilla aquí ,

El método then () devuelve una Promesa. Se necesitan dos argumentos: funciones de devolución de llamada para los casos de éxito y fracaso de Promise.

El objeto Promesa, a su vez, se define como

El objeto Promise se usa para cálculos diferidos y asincrónicos. Una promesa representa una operación que aún no se ha completado, pero que se espera en el futuro.

Es decir, Promiseactúa como un marcador de posición para un valor que aún no se calcula, pero se resolverá en el futuro. Y la .then()función se usa para asociar las funciones que se invocarán en la Promesa cuando se resuelva, ya sea como un éxito o un fracaso.

usuario113716
fuente
12
No había una función incorporada en .thenese momento, pero las promesas nativas están llegando ahora en ES6: html5rocks.com/en/tutorials/es6/promises
janfoeh
gracias por esta respuesta, esperaba una devolución de llamada prometedora, pero resultó ser una función real llamada 'entonces' que se devolvió.
spartikus
15

Aquí hay una cosa que hice para aclarar cómo funcionan las cosas. Supongo que otros también pueden encontrar útil este ejemplo concreto:

doit().then(function() { log('Now finally done!') });
log('---- But notice where this ends up!');

// For pedagogical reasons I originally wrote the following doit()-function so that 
// it was clear that it is a promise. That way wasn't really a normal way to do 
// it though, and therefore Slikts edited my answer. I therefore now want to remind 
// you here that the return value of the following function is a promise, because 
// it is an async function (every async function returns a promise). 
async function doit() {
  log('Calling someTimeConsumingThing');
  await someTimeConsumingThing();
  log('Ready with someTimeConsumingThing');
}

function someTimeConsumingThing() {
  return new Promise(function(resolve,reject) {
    setTimeout(resolve, 2000);
  })
}

function log(txt) {
  document.getElementById('msg').innerHTML += txt + '<br>'
}
<div id='msg'></div>

Magnus
fuente
5

Aquí hay un pequeño JS_Fiddle.

luego es una pila de devolución de llamada de método que está disponible después de que se resuelve una promesa, es parte de una biblioteca como jQuery pero ahora está disponible en JavaScript nativo y a continuación se muestra la explicación detallada de cómo funciona

Puede hacer una Promesa en JavaScript nativo: al igual que hay promesas en jQuery, cada promesa se puede apilar y luego se puede invocar con devoluciones de llamada Resolver y Rechazar. Así es como se pueden encadenar llamadas asincrónicas.

Bifurqué y edité de MSDN Docs sobre el estado de carga de la batería.

Lo que esto hace es tratar de averiguar si la computadora portátil o dispositivo del usuario está cargando la batería. luego se llama y puede hacer su trabajo después del éxito.

navigator
    .getBattery()
    .then(function(battery) {
       var charging = battery.charging;
       alert(charging);
    })
    .then(function(){alert("YeoMan : SINGH is King !!");});

Otro ejemplo de es6

function fetchAsync (url, timeout, onData, onError) {
    
}
let fetchPromised = (url, timeout) => {
    return new Promise((resolve, reject) => {
        fetchAsync(url, timeout, resolve, reject)
    })
}
Promise.all([
    fetchPromised("http://backend/foo.txt", 500),
    fetchPromised("http://backend/bar.txt", 500),
    fetchPromised("http://backend/baz.txt", 500)
]).then((data) => {
    let [ foo, bar, baz ] = data
    console.log(`success: foo=${foo} bar=${bar} baz=${baz}`)
}, (err) => {
    console.log(`error: ${err}`)
})

Definition :: then es un método utilizado para resolver devoluciones de llamada asincrónicas

esto se introduce en ES6

Encuentre la documentación adecuada aquí Es6 Promesas

Tarandeep Singh
fuente
Su respuesta en realidad no responde la pregunta. Solo proporciona un ejemplo del uso de la API sin explicar de dónde thenviene y cómo funciona. Debería mejorar su respuesta para proporcionar esos detalles.
Didier L
@TarandeepSingh: en primer lugar, en la declaración donde alerta el estado de la batería no se devuelve ningún objeto de promesa. Entonces, ¿para qué sirve el segundo
Mohit Jain
@MohitJain Muestra que puedes hacer múltiples devoluciones de llamada incluso si no tienes ninguna promesa nueva. Desde entonces, las llamadas múltiples también se pueden hacer con Promise.all.
Tarandeep Singh
¿A qué te refieres con " pila de devolución de llamada de método "?
Bergi
4

Sospecho que doSome devuelve esto, que es myObj, que también tiene un método then. Método estándar de encadenamiento ...

si doSome no devuelve esto, siendo el objeto en el que se ejecutó doSome, puede estar seguro de que devuelve algún objeto con un método then ...

como señala @patrick, no hay entonces () para js estándar

hvgotcodes
fuente
1
Sospecho que algunos devuelven esto - nada impone / justifica tales sospechas
Salathiel Genèse
1

doSome ("tarea") debe devolver un objeto de promesa, y esa promesa siempre tiene una función de continuación. Por lo tanto, su código es así

promise.then(function(env) {
    // logic
}); 

y sabes que esto es solo una llamada ordinaria a la función miembro.

Hardeep SINGH
fuente
1

.then devuelve una promesa en la función asíncrona.

Un buen ejemplo sería:

var doSome = new Promise(function(resolve, reject){
    resolve('I am doing something');
});

doSome.then(function(value){
    console.log(value);
});

Para agregarle otra lógica, también puede agregar la reject('I am the rejected param')llamada a la función y console.log.

diseños de aplicaciones
fuente
0

En este caso then()es un método de clase del objeto devuelto por doSome()método.


fuente
0

La función ".then ()" se usa ampliamente para objetos prometidos en la programación de Asynchoronus para aplicaciones de la Tienda Windows 8. Según tengo entendido, funciona de alguna manera como una devolución de llamada.

Encuentre detalles en este Documentantion http://msdn.microsoft.com/en-us/library/windows/apps/hh700330.aspx

Causa también podría ser el nombre de cualquier otra función definida.

user2508620
fuente
0

Otro ejemplo:

new Promise(function(ok) {
   ok( 
      /* myFunc1(param1, param2, ..) */
   )
}).then(function(){
     /* myFunc1 succeed */
     /* Launch something else */
     /* console.log(whateverparam1) */
     /* myFunc2(whateverparam1, otherparam, ..) */
}).then(function(){
     /* myFunc2 succeed */
     /* Launch something else */
     /* myFunc3(whatever38, ..) */
})

La misma lógica usando las funciones de flecha abreviada:

new Promise((ok) =>
   ok( 
      /* myFunc1(param1, param2, ..) */
)).then(() =>
     /* myFunc1 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc2(whateverparam1, otherparam, ..) */
).then(() =>
     /* myFunc2 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc3(whatever38, ..) */
)

NVRM
fuente
-4

Llego unos 8 años tarde, bueno ... de todos modos, realmente no sé qué hace entonces () pero quizás MDN podría tener una respuesta. En realidad, podría entenderlo un poco más.

Esto le mostrará toda la información (con suerte) que necesita. A menos que alguien ya haya publicado este enlace. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

El formato es promise.prototype.then () La promesa y el prototipo son como variables similares pero no como variables en javascript, quiero decir que otras cosas van allí como navigator.getBattery (). Luego () donde esta realmente existe pero es Apenas utilizado en la web, este muestra estados sobre la batería del dispositivo, más información y más sobre MDN si tiene curiosidad.

Jonathan J. Pecany
fuente