He estado usando ES6 Promise.
Por lo general, una promesa se construye y se usa así
new Promise(function(resolve, reject){
if (someCondition){
resolve();
} else {
reject();
}
});
Pero he estado haciendo algo como a continuación para llevar la resolución afuera en aras de la flexibilidad.
var outsideResolve;
var outsideReject;
new Promise(function(resolve, reject) {
outsideResolve = resolve;
outsideReject = reject;
});
Y después
onClick = function(){
outsideResolve();
}
Esto funciona bien, pero ¿hay una manera más fácil de hacer esto? Si no es así, ¿es una buena práctica?
No, no hay otra forma de hacer esto, lo único que puedo decir es que este caso de uso no es muy común. Como dijo Felix en el comentario, lo que hagas funcionará constantemente.
Vale la pena mencionar que la razón por la que el constructor de promesas se comporta de esta manera es la seguridad de lanzamiento: si ocurre una excepción que no anticipó mientras su código se ejecuta dentro del constructor de promesa, se convertirá en un rechazo, esta forma de seguridad de lanzamiento: conversión de errores lanzados Los rechazos son importantes y ayudan a mantener un código predecible.
Por esta razón de seguridad de lanzamiento, se eligió el constructor de promesas en lugar de diferidos (que son una forma alternativa de construcción de promesas que permite lo que está haciendo); en cuanto a las mejores prácticas, pasaría el elemento y usaría el constructor de promesas en su lugar:
var p = new Promise(function(resolve, reject){
this.onclick = resolve;
}.bind(this));
Por esta razón, siempre que pueda usar el constructor de promesas en lugar de exportar las funciones, le recomiendo que lo use. Siempre que pueda evitar ambos, evite ambos y la cadena.
Tenga en cuenta que nunca debe usar el constructor de promesas para cosas como if(condition)
, el primer ejemplo podría escribirse como:
var p = Promise[(someCondition)?"resolve":"reject"]();
sencillo:
var promiseResolve, promiseReject;
var promise = new Promise(function(resolve, reject){
promiseResolve = resolve;
promiseReject = reject;
});
promiseResolve();
Un poco tarde para la fiesta aquí, pero otra forma de hacerlo sería usar un objeto diferido . Básicamente, tiene la misma cantidad de texto repetitivo, pero es útil si desea pasarlos y posiblemente resolverlos fuera de su definición.
Implementación ingenua:
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject)=> {
this.reject = reject
this.resolve = resolve
})
}
}
function asyncAction() {
var dfd = new Deferred()
setTimeout(()=> {
dfd.resolve(42)
}, 500)
return dfd.promise
}
asyncAction().then(result => {
console.log(result) // 42
})
Versión ES5:
function Deferred() {
var self = this;
this.promise = new Promise(function(resolve, reject) {
self.reject = reject
self.resolve = resolve
})
}
function asyncAction() {
var dfd = new Deferred()
setTimeout(function() {
dfd.resolve(42)
}, 500)
return dfd.promise
}
asyncAction().then(function(result) {
console.log(result) // 42
})
Una solución que se me ocurrió en 2015 para mi marco. Llamé a este tipo de promesas Tarea
function createPromise(handler){
var resolve, reject;
var promise = new Promise(function(_resolve, _reject){
resolve = _resolve;
reject = _reject;
if(handler) handler(resolve, reject);
})
promise.resolve = resolve;
promise.reject = reject;
return promise;
}
// create
var promise = createPromise()
promise.then(function(data){ alert(data) })
// resolve from outside
promise.resolve(200)
Me gustó la respuesta de @JonJaques, pero quería dar un paso más.
Si enlaza then
y catch
luego el Deferred
objeto, entonces implementa completamente la Promise
API y puede tratarlo como una promesa y await
eso y tal.
class DeferredPromise {
constructor() {
this._promise = new Promise((resolve, reject) => {
// assign the resolve and reject functions to `this`
// making them usable on the class instance
this.resolve = resolve;
this.reject = reject;
});
// bind `then` and `catch` to implement the same interface as Promise
this.then = this._promise.then.bind(this._promise);
this.catch = this._promise.catch.bind(this._promise);
this[Symbol.toStringTag] = 'Promise';
}
}
const deferred = new DeferredPromise();
console.log('waiting 2 seconds...');
setTimeout(() => {
deferred.resolve('whoa!');
}, 2000);
async function someAsyncFunction() {
const value = await deferred;
console.log(value);
}
someAsyncFunction();
Un método auxiliar aliviaría esta sobrecarga adicional y le daría la misma sensación de jQuery.
function Deferred() {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
El uso sería
const { promise, resolve, reject } = Deferred();
displayConfirmationDialog({
confirm: resolve,
cancel: reject
});
return promise;
Que es similar a jQuery
const dfd = $.Deferred();
displayConfirmationDialog({
confirm: dfd.resolve,
cancel: dfd.reject
});
return dfd.promise();
Aunque, en un caso de uso, esta sintaxis simple y nativa está bien
return new Promise((resolve, reject) => {
displayConfirmationDialog({
confirm: resolve,
cancel: reject
});
});
La respuesta aceptada es incorrecta. Es bastante fácil usar el alcance y las referencias, aunque puede enojar a los puristas de Promise :
const createPromise = () => {
let resolver;
return [
new Promise((resolve, reject) => {
resolver = resolve;
}),
resolver,
];
};
const [ promise, resolver ] = createPromise();
promise.then(value => console.log(value));
setTimeout(() => resolver('foo'), 1000);
Básicamente, tomamos la referencia a la función de resolución cuando se crea la promesa, y la devolvemos para que se pueda establecer externamente.
En un segundo, la consola generará:
> foo
Estoy usando una función auxiliar para crear lo que llamo una "promesa plana".
function flatPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
Y lo estoy usando así ...
function doSomethingAsync() {
// Get your promise and callbacks
const { resolve, reject, promise } = flatPromise();
// Do something amazing...
setTimeout(() => {
resolve('done!');
}, 500);
// Pass your promise to the world
return promise;
}
Ver ejemplo de trabajo completo -
function flatPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
function doSomethingAsync() {
// Get your promise and callbacks
const { resolve, reject, promise } = flatPromise();
// Do something amazing...
setTimeout(() => {
resolve('done!');
}, 500);
// Pass your promise to the world
return promise;
}
(async function run() {
const result = await doSomethingAsync()
.catch(err => console.error('rejected with', err));
console.log(result);
})();
Editar: he creado un paquete NPM llamado promesa plana y el código también está disponible en GitHub .
Puede envolver la Promesa en una clase.
class Deferred {
constructor(handler) {
this.promise = new Promise((resolve, reject) => {
this.reject = reject;
this.resolve = resolve;
handler(resolve, reject);
});
this.promise.resolve = this.resolve;
this.promise.reject = this.reject;
return this.promise;
}
promise;
resolve;
reject;
}
// How to use.
const promise = new Deferred((resolve, reject) => {
// Use like normal Promise.
});
promise.resolve(); // Resolve from any context.
Muchas de las respuestas aquí son similares al último ejemplo de este artículo . Estoy almacenando en caché múltiples promesas, y las funciones resolve()
y reject()
se pueden asignar a cualquier variable o propiedad. Como resultado, puedo hacer que este código sea un poco más compacto:
function defer(obj) {
obj.promise = new Promise((resolve, reject) => {
obj.resolve = resolve;
obj.reject = reject;
});
}
A continuación, se muestra un ejemplo simplificado del uso de esta versión de defer()
para combinar una FontFace
promesa de carga con otro proceso asíncrono:
function onDOMContentLoaded(evt) {
let all = []; // array of Promises
glob = {}; // global object used elsewhere
defer(glob);
all.push(glob.promise);
// launch async process with callback = resolveGlob()
const myFont = new FontFace("myFont", "url(myFont.woff2)");
document.fonts.add(myFont);
myFont.load();
all.push[myFont];
Promise.all(all).then(() => { runIt(); }, (v) => { alert(v); });
}
//...
function resolveGlob() {
glob.resolve();
}
function runIt() {} // runs after all promises resolved
Actualización: 2 alternativas en caso de que desee encapsular el objeto:
function defer(obj = {}) {
obj.promise = new Promise((resolve, reject) => {
obj.resolve = resolve;
obj.reject = reject;
});
return obj;
}
let deferred = defer();
y
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
let deferred = new Deferred();
También me encuentro perdiendo el patrón diferido en ciertos casos. Siempre puede crear uno además de una Promesa ES6:
export default class Deferred<T> {
private _resolve: (value: T) => void = () => {};
private _reject: (value: T) => void = () => {};
private _promise: Promise<T> = new Promise<T>((resolve, reject) => {
this._reject = reject;
this._resolve = resolve;
})
public get promise(): Promise<T> {
return this._promise;
}
public resolve(value: T) {
this._resolve(value);
}
public reject(value: T) {
this._reject(value);
}
}
Nuestra solución fue utilizar cierres para almacenar las funciones de resolución / rechazo y, además, adjuntar una función para ampliar la promesa en sí.
Aquí está el patrón:
function getPromise() {
var _resolve, _reject;
var promise = new Promise((resolve, reject) => {
_reject = reject;
_resolve = resolve;
});
promise.resolve_ex = (value) => {
_resolve(value);
};
promise.reject_ex = (value) => {
_reject(value);
};
return promise;
}
Y usándolo:
var promise = getPromise();
promise.then(value => {
console.info('The promise has been fulfilled: ' + value);
});
promise.resolve_ex('hello');
// or the reject version
//promise.reject_ex('goodbye');
Sí tu puedes. Utilizando la CustomEvent
API para el entorno del navegador. Y usando un proyecto de emisor de eventos en entornos node.js. Dado que el fragmento de la pregunta es para el entorno del navegador, aquí hay un ejemplo práctico para el mismo.
function myPromiseReturningFunction(){
return new Promise(resolve => {
window.addEventListener("myCustomEvent", (event) => {
resolve(event.detail);
})
})
}
myPromiseReturningFunction().then(result => {
alert(result)
})
document.getElementById("p").addEventListener("click", () => {
window.dispatchEvent(new CustomEvent("myCustomEvent", {detail : "It works!"}))
})
<p id="p"> Click me </p>
¡Espero que esta respuesta sea útil!
Gracias a todos los que publicaron en este hilo. Creé un módulo que incluye el objeto Defer () descrito anteriormente, así como algunos otros objetos construidos sobre él. Todos aprovechan Promises y la elegante sintaxis de devolución de llamada de Promise para implementar el manejo de comunicaciones / eventos dentro de un programa.
Cola: cola de ejecución basada en el encadenamiento de promesas.
rp = require("repeatable-promise")
Escribí una pequeña biblioteca para esto. https://www.npmjs.com/package/@inf3rno/promise.exposed
He utilizado el enfoque método de fábrica otros escribieron, pero hizo caso omiso de la then
, catch
, finally
métodos también, para que puedan resolver la promesa original de esos también.
Promesa resolutiva sin ejecutor externo:
const promise = Promise.exposed().then(console.log);
promise.resolve("This should show up in the console.");
Corriendo con el setTimeout del ejecutor desde fuera:
const promise = Promise.exposed(function (resolve, reject){
setTimeout(function (){
resolve("I almost fell asleep.")
}, 100000);
}).then(console.log);
setTimeout(function (){
promise.resolve("I don't want to wait that much.");
}, 100);
Hay un modo sin conflicto si no desea contaminar el espacio de nombres global:
const createExposedPromise = require("@inf3rno/promise.exposed/noConflict");
const promise = createExposedPromise().then(console.log);
promise.resolve("This should show up in the console.");
Hice una biblioteca llamada manual-promise
que funciona como un reemplazo directo de Promise
. Ninguna de las otras respuestas aquí funcionará como reemplazo directo Promise
, ya que usan proxies o envoltorios.
yarn add manual-promise
npn install manual-promise
import { ManualPromise } from "manual-promise";
const prom = new ManualPromise();
prom.resolve(2);
// actions can still be run inside the promise
const prom2 = new ManualPromise((resolve, reject) => {
// ... code
});
new ManualPromise() instanceof Promise === true
¿Qué tal crear una función para secuestrar el rechazo y devolverlo?
function createRejectablePromise(handler) {
let _reject;
const promise = new Promise((resolve, reject) => {
_reject = reject;
handler(resolve, reject);
})
promise.reject = _reject;
return promise;
}
// Usage
const { reject } = createRejectablePromise((resolve) => {
setTimeout(() => {
console.log('resolved')
resolve();
}, 2000)
});
reject();
He reunido una esencia que hace ese trabajo: https://gist.github.com/thiagoh/c24310b562d50a14f3e7602a82b4ef13
así es como debe usarlo:
import ExternalizedPromiseCreator from '../externalized-promise';
describe('ExternalizedPromise', () => {
let fn: jest.Mock;
let deferredFn: jest.Mock;
let neverCalledFn: jest.Mock;
beforeEach(() => {
fn = jest.fn();
deferredFn = jest.fn();
neverCalledFn = jest.fn();
});
it('resolve should resolve the promise', done => {
const externalizedPromise = ExternalizedPromiseCreator.create(() => fn());
externalizedPromise
.promise
.then(() => deferredFn())
.catch(() => neverCalledFn())
.then(() => {
expect(deferredFn).toHaveBeenCalled();
expect(neverCalledFn).not.toHaveBeenCalled();
done();
});
expect(fn).toHaveBeenCalled();
expect(neverCalledFn).not.toHaveBeenCalled();
expect(deferredFn).not.toHaveBeenCalled();
externalizedPromise.resolve();
});
...
});
Solo otra solución para resolver Promise desde fuera
class Lock {
#lock; // Promise to be resolved (on release)
release; // Release lock
id; // Id of lock
constructor(id) {
this.id = id
this.#lock = new Promise((resolve) => {
this.release = () => {
if (resolve) {
resolve()
} else {
Promise.resolve()
}
}
})
}
get() { return this.#lock }
}
let lock = new Lock(... some id ...);
...
lock.get().then(()=>{console.log('resolved/released')})
lock.release() // Excpected 'resolved/released'
Como no encontré lo que estaba buscando, compartiré lo que realmente quería lograr cuando terminé en esta pregunta.
Escenario : tengo 3 API diferentes con la misma respuesta posible y, por lo tanto, me gustaría manejar la finalización y el manejo de errores de las promesas en una sola función. Esto es lo que hice:
private handleHttpPromise = (promise: Promise<any>) => {
promise
.then((response: any) => {
// do something with the response
console.log(response);
})
.catch((error) => {
// do something with the error
console.log(error);
});
};
switch (method) {
case 'get': {
this.handleHttpPromise(apiService.get(url));
break;
}
case 'post': {
if (jsonData) {
this.handleHttpPromise(apiService.post(url, jsonData));
}
break;
}
// (...)
}
La estrella de HGTV, Christina Hall, revela que le diagnosticaron envenenamiento por mercurio y plomo, probablemente debido a su trabajo como manipuladora de casas.
Recientemente salió a la luz un informe policial que acusa a la estrella de 'Love Is Blind', Brennon, de violencia doméstica. Ahora, Brennon ha respondido a los reclamos.
Conozca cómo Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia mientras organizaba la primera celebración de Acción de Gracias desde que murió su madre, Naomi Judd.
Descubra por qué un destacado experto en lenguaje corporal cree que es fácil trazar "tales paralelismos" entre la princesa Kate Middleton y la princesa Diana.
Los inodoros arrojan columnas de aerosol invisibles con cada descarga. ¿Como sabemos? La prueba fue capturada por láseres de alta potencia.
Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?
The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!
¿Sigue siendo efectivo ese lote de repelente de insectos que te quedó del verano pasado? Si es así, ¿por cuánto tiempo?
Si este tráiler de pesadilla de la temporada más reciente de Great British Bake Off te asustó y te hizo no volver a ver el programa, es posible que tengas suerte: PBS no ha decidido si se transmitirá o no la última temporada en los Estados Unidos. actualización, para aquellos que no siguen sin aliento este tipo de drama de nicho: el presentador Paul Hollywood y la hermosa carpa llena de batidoras de colores pastel y cuadros se trasladaron de la BBC al Canal 4; Mary Berry, Sue Perkins y Mel Giedroyc renunciaron.
Foto: Netflix Oh, Hello On Broadway (Netflix): Después de llegar a Broadway el año pasado, los dos locos del Upper West Side interpretados por John Mulaney y Nick Kroll finalmente llegaron a Netflix. El especial consta del espectáculo en el escenario, algunos momentos entre bastidores y un invitado muy especial de “Too Much Tuna”.
¿Quiere probar un cepillo de dientes Sonicare sin gastar mucho dinero en uno de sus modelos favoritos de gama alta? Puede comprar un kit de la Serie 2 o Serie 3 por tan solo $ 30 hoy en Amazon. Haga clic aquí para ver la lista completa de modelos elegibles y tenga en cuenta que se descontarán $ 10 adicionales en su carrito.
Tapas elásticas de silicona de Tomorrow's Kitchen, paquete de 12 | $14 | Amazonas | Código promocional 20OFFKINJALids son básicamente los calcetines de la cocina; siempre perdiéndose, dejando contenedores huérfanos que nunca podrán volver a cerrarse. Pero, ¿y si sus tapas pudieran estirarse y adaptarse a todos los recipientes, ollas, sartenes e incluso frutas en rodajas grandes que sobran? Nunca más tendrás que preocuparte por perder esa tapa tan específica.
El equipo está a la espera de las medallas que ganó en los Juegos Olímpicos de Invierno de 2022 en Beijing, ya que se está resolviendo un caso de dopaje que involucra a la patinadora artística rusa Kamila Valieva.
Miles de compradores de Amazon recomiendan la funda de almohada de seda Mulberry, y está a la venta en este momento. La funda de almohada de seda viene en varios colores y ayuda a mantener el cabello suave y la piel clara. Compre las fundas de almohada de seda mientras tienen hasta un 46 por ciento de descuento en Amazon
El jueves se presentó una denuncia de delito menor amenazante agravado contra Joe Mixon.
El Departamento de Policía de Lafayette comenzó a investigar a un profesor de la Universidad de Purdue en diciembre después de recibir varias denuncias de un "hombre sospechoso que se acercaba a una mujer".
Al igual que el mundo que nos rodea, el lenguaje siempre está cambiando. Mientras que en eras anteriores los cambios en el idioma ocurrían durante años o incluso décadas, ahora pueden ocurrir en cuestión de días o incluso horas.
Estoy de vuelta por primera vez en seis años. No puedo decirte cuánto tiempo he estado esperando esto.
“And a river went out of Eden to water the garden, and from thence it was parted and became into four heads” Genesis 2:10. ? The heart is located in the middle of the thoracic cavity, pointing eastward.
Creo, un poco tarde en la vida, en dar oportunidades a la gente. Generosamente.