javascript - ejemplo - ES6 promesa de devolución de llamada establecida?
reduce javascript ejemplo (7)
Quiero ejecutar la misma acción si mi promesa se resolvió con éxito o no.
No quiero vincular la misma función a ambos
.then
de
.then
.
¿No hay un.
.always
como jQuery tiene?
Si no, ¿cómo puedo lograr esto?
¿No hay un.
.always
como jQuery tiene?
No, no hay (todavía) . Aunque hay una propuesta activa , entonces tal vez ES2018.
Si no, ¿cómo puedo lograr esto?
Puede implementar el método de
finally
usted mismo de esta manera:
Promise.prototype.finally = function(cb) {
const res = () => this
const fin = () => Promise.resolve(cb()).then(res)
return this.then(fin, fin);
};
o más ampliamente, con pasar información de resolución a la devolución de llamada:
Promise.prototype.finally = function(cb) {
const res = () => this
return this.then(value =>
Promise.resolve(cb({state:"fulfilled", value})).then(res)
, reason =>
Promise.resolve(cb({state:"rejected", reason})).then(res)
);
};
Ambos aseguran que la resolución original sea sostenida (cuando no hay excepción en la devolución de llamada) y que se esperan promesas.
Resumen:
Ahora también tenemos acceso a
Promise.prototype.finally()
.
Esta es una función que se puede poner en la cadena de promesa como un último elemento para realizar una limpieza.
funciona de la siguiente manera en comparación con
Promise.then
y
Promise.catch
:
-
Promise.then
solo recibe una llamada cuando se resuelve la promesa (si solo le pone la función de devolución de llamada de primer argumento) -
Promise.catch
solo se llama cuando se rechaza la promesa -
Promise.finally
siempre se llama cuando sePromise.finally
una promesa, por lo tanto, cuando la promesa se rechaza o se resuelve.
Ejemplo:
let Prom = new Promise((res, rej) => {
let random = Math.random();
if (random > 0.5) {
res(1);
} else {
rej(''Error occured'')
}
});
Prom.then((val) => {
console.log(val);
return val * 10;
}).catch((err) => {
console.log(err);
}).finally(() => {
console.log(''finally executed'');
})
En el ejemplo anterior, podemos observar que
finally
siempre se ejecuta independientemente de si la promesa se resuelve o rechaza.
No es que
finally
, idealmente, siempre esté al final de la cadena de promesa para hacer una limpieza que se ejecute independientemente del resultado de la promesa.
La ventaja de usar
finally
es que evita la necesidad de duplicar el código porque se ejecuta tanto para una promesa resuelta como para una rechazada.
De lo contrario, tendríamos que usar hacks como:
.then(onfullfilled, onfullfilled)
o
.then(onfullfilled)
.catch(onfullfilled)
Tenga en cuenta que ahora tenemos que definir la función
onfullfilled
como una función con nombre fuera del propio controlador de promesa (o pasar 2 copias de funciones anónimas que es aún menos elegante).
Promise.finally
resuelve este problema.
Aquí está mi implementación de .finally ().
Promise.prototype.finally = function(cb) {
return this.then(v=>Promise.resolve(cb(v)),
v=>Promise.reject(cb(v)));
};
Lo probé:
(new Promise((resolve,reject)=>{resolve(5);})).finally(x=>console.log(x)); //5
(new Promise((resolve,reject)=>{reject(6);})).finally(x=>console.log(x)); //6
(new Promise((resolve,reject)=>{reject(7);}))
.then(x=>x,y=>y)
.catch(x=>{throw "error";})
.finally(x=>{console.log(x); throw "error"; return x;}) // 7
.then(x=>console.log(x),y=>console.log(''e'')); //e
// Uncaught (in promise) undefined
Con async / await, puede una combinación de
await
con
try/finally
, así:
async function(somePromise) {
try {
await somePromise();
} finally {
// always run this-- even if `somePromise` threw something
}
}
Aquí hay un ejemplo real que tengo en producción con Node, usando el complemento async-to-generator Babel.
// Wrap promisified function in a transaction block
export function transaction(func) {
return db.sequelize.transaction().then(async t => {
Sequelize.cls.set(''transaction'', t);
try {
await func();
} finally {
await t.rollback();
}
});
}
Utilizo este código dentro de una prueba de mocha junto con Sequelize ORM para iniciar una transacción de base de datos, e independientemente del resultado de las llamadas de base de datos dentro de la prueba, siempre retrocedo al final.
Esto es más o menos análogo al método
.finally()
Bluebird, pero en mi opinión, ¡una sintaxis mucho mejor!
(
Nota
: en caso de que se pregunte por qué no estoy
await
la primera Promesa, es un detalle de implementación de Sequelize. Utiliza
CLS
para ''vincular'' una transacción SQL a una cadena Promise. Cualquier cosa que ocurra
dentro de
la misma cadena está limitado a la transacción. Cualquier cosa externa no lo está. Por lo tanto, esperar en la Promesa habría "cerrado" el bloque de la transacción y roto la cadena. Lancé este ejemplo para mostrarle cómo el manejo de la promesa "vainilla" se puede mezclar junto con asíncrono funciones y jugar bien juntos.)
No es necesario introducir nuevos conceptos.
const promise = new Promise((resolve, reject) => {
/*some code here*/
});
promise.then(() => {
/* execute success code */
}, () => {
/* execute failure code here */
}).then(() => {}, () => {}).then(() => {
/* finally code here */
});
Para extender la respuesta de Bergi .
Devolver Promise.reject () en el controlador catch evitará que se llame a finnalizing ''then''.
Entonces, si va a manejar el error de promesa más de 2 veces, debe usar repetitivo como este:
return myPromise()
.then(() => ... )
.catch((error) => {
...
myFinnaly();
return Promise.reject(error);
})
.then(() => myFinnaly());
Si no puede / no puede actualizar el prototipo, la forma de hackear finalmente es:
executeMyPromise()
.then(function(res){ return {res: res}; })
.catch(function(err){ return {err: err}; })
.then(function(data) {
// do finally stuff
if (data.err) {
throw data.err;
}
return data.res;
}).catch(function(err) {
// handle error
});