w3schools sincronas promises promesas nodejs funciones from ejemplo javascript promise es6-promise

sincronas - ¿Cómo puedo determinar sincrónicamente el estado de una promesa de JavaScript?



promises js (19)

Tengo una promesa de JavaScript pura (implementación incorporada o relleno de polietileno):

var promise = new Promise(function (resolve, reject) { /* ... */ });

De la specification , una Promesa puede ser uno de:

  • ''resuelto'' y ''resuelto''
  • ''resuelto'' y ''rechazado''
  • ''pendiente''

Tengo un caso de uso en el que deseo interrogar a Promise sincrónicamente y determinar:

  • ¿Está resuelta la promesa?

  • Si es así, ¿se resuelve la promesa?

Sé que puedo usar #then() para programar el trabajo que se realizará de forma asíncrona después de que Promise cambie de estado. NO estoy preguntando cómo hacer esto.

Esta pregunta es específicamente sobre la interrogación sincrónica del estado de una promesa . ¿Cómo puedo conseguir esto?


Actualizado: 2019

Bluebird.js ofrece esto: http://bluebirdjs.com/docs/api/isfulfilled.html

var Promise = require("bluebird"); let p = Promise.resolve(); console.log(p.isFulfilled());

Si prefiere crear su propio contenedor, aquí hay un buen blog al respecto.

Debido a que JavaScript es de un solo subproceso, es difícil encontrar un caso de uso lo suficientemente común como para justificar poner esto en las especificaciones. El mejor lugar para saber si se resuelve una promesa es en .then (). Probar si se cumple una Promesa crearía un bucle de sondeo que probablemente sea la dirección incorrecta.

async/await es una buena construcción si desea razonar el código asincrónico sincrónicamente.

await this(); await that(); return ''success!'';

Otra llamada útil es Promise.all()

var promise1 = Promise.resolve(3); var promise2 = 42; var promise3 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, ''foo''); }); Promise.all([promise1, promise2, promise3]).then(function(values) { console.log(values); }); // expected output: Array [3, 42, "foo"]

Cuando llegué por primera vez a esta respuesta, ese es el caso de uso que estaba buscando.


A partir de la versión 8 de Node.js, ahora puede usar el paquete de wise-inspection sincrónicamente las promesas nativas (sin ningún truco peligroso).


Advertencia: este método utiliza elementos internos de Node.js no documentados y se puede cambiar sin previo aviso.

En Node puede determinar sincrónicamente el estado de una promesa utilizando process.binding(''util'').getPromiseDetails(/* promise */); .

Esto devolverá:

[0, ] para pendiente,

[1, /* value */] para cumplido, o

[2, /* value */] para rechazado.

const pending = new Promise(resolve => setTimeout(() => resolve(''yakko'')));; const fulfilled = Promise.resolve(''wakko''); const rejected = Promise.reject(''dot''); [pending, fulfilled, rejected].forEach(promise => { console.log(process.binding(''util'').getPromiseDetails(promise)); }); // pending: [0, ] // fulfilled: [1, ''wakko''] // rejected: [2, ''dot'']

Envolviendo esto en una función auxiliar:

const getStatus = promise => [''pending'', ''fulfilled'', ''rejected''][ process.binding(''util'').getPromiseDetails(promise)[0] ]; getStatus(pending); // pending getStatus(fulfilled); // fulfilled getStatus(rejected); // rejected


Aquí hay una versión es6 más desarrollada de QueryablePromise, que permite encadenar y capturar después de la primera resolución y resolver o rechazar inmediatamente para mantener la API coherente con la Promesa nativa.

const PROMISE = Symbol(''PROMISE'') const tap = fn => x => (fn(x), x) const trace = label => tap(x => console.log(label, x)) class QueryablePromise { resolved = false rejected = false fulfilled = false catchFns = [] constructor(fn) { this[PROMISE] = new Promise(fn) .then(tap(() => { this.fulfilled = true this.resolved = true })) .catch(x => { this.fulfilled = true this.rejected = true return Promise.reject(x) }) } then(fn) { this[PROMISE].then(fn) return this } catch(fn) { this[PROMISE].catch(fn) return this } static resolve(x) { return new QueryablePromise((res) => res(x)) } static reject(x) { return new QueryablePromise((_, rej) => rej(x)) } } const resolvedPromise = new QueryablePromise((res) => { setTimeout(res, 200, ''resolvedPromise'') }) const rejectedPromise = new QueryablePromise((_, rej) => { setTimeout(rej, 200, ''rejectedPromise'') }) // ensure our promises have not been fulfilled console.log(''test 1 before: is resolved'', resolvedPromise.resolved) console.log(''test 2 before: is rejected'', rejectedPromise.rejected) setTimeout(() => { // check to see the resolved status of our promise console.log(''test 1 after: is resolved'', resolvedPromise.resolved) console.log(''test 2 after: is rejected'', rejectedPromise.rejected) }, 300) // make sure we can immediately resolve a QueryablePromise const immediatelyResolvedPromise = QueryablePromise.resolve(''immediatelyResolvedPromise'') // ensure we can chain then .then(trace(''test 3 resolved'')) .then(trace(''test 3 resolved 2'')) .catch(trace(''test 3 rejected'')) // make sure we can immediately reject a QueryablePromise const immediatelyRejectedPromise = QueryablePromise.reject(''immediatelyRejectedPromise'') .then(trace(''test 4 resolved'')) .catch(trace(''test 4 rejected''))

<script src="https://codepen.io/synthet1c/pen/KyQQmL.js"></script>


De hecho, es bastante molesto que falte esta funcionalidad básica. Si está utilizando node.js, sé dos soluciones alternativas, ninguna de las cuales es muy bonita. Ambos fragmentos a continuación implementan la misma API:

function wrapPromise(promise) { var value, error, settled = false, resolved = false, rejected = false, p = promise.then(function(v) { value = v; settled = true; resolved = true; return v; }, function(err) { error = err; settled = true; rejected = true; throw err; }); p.isSettled = function() { return settled; }; p.isResolved = function() { return resolved; }; p.isRejected = function() { return rejected; }; p.value = function() { return value; }; p.error = function() { return error; }; var pThen = p.then, pCatch = p.catch; p.then = function(res, rej) { return wrapPromise(pThen(res, rej)); }; p.catch = function(rej) { return wrapPromise(pCatch(rej)); }; return p; }

No parece haber ninguna forma de distinguir los dos últimos estados de promesa utilizando cualquiera de los trucos.

1. Use la API de depuración V8

Este es el mismo truco que utiliza util.inspect .

> Promise.getInfo( 42 ) // not a promise { status: ''fulfilled'', value: 42 } > Promise.getInfo( Promise.resolve(42) ) // fulfilled { status: ''fulfilled'', value: 42 } > Promise.getInfo( Promise.reject(42) ) // rejected { status: ''rejected'', value: 42 } > Promise.getInfo( p = new Promise(() => {}) ) // unresolved { status: ''pending'' } > Promise.getInfo( Promise.resolve(p) ) // resolved but pending { status: ''pending'' }

2. Ejecute sincrónicamente microtasks

Esto evita la API de depuración, pero tiene una semántica aterradora al hacer que todas las microtasks pendientes y las devoluciones de llamada de process.nextTick se ejecuten sincrónicamente. También tiene el efecto secundario de evitar que se active el error de "rechazo de promesa no manejado" para la promesa inspeccionada.

const Debug = require(''vm'').runInDebugContext(''Debug''); Promise.getInfo = function( arg ) { let mirror = Debug.MakeMirror( arg, true ); if( ! mirror.isPromise() ) return { status: ''fulfilled'', value: arg }; let status = mirror.status(); if( status === ''pending'' ) return { status }; if( status === ''resolved'' ) // fix terminology fuck-up status = ''fulfilled''; let value = mirror.promiseValue().value(); return { status, value }; };


Encontré que esta solución es simple y me permite continuar usando promesas nativas, pero agregar comprobaciones síncronas útiles. Tampoco tuve que sacar una biblioteca de promesas completa.

PRUEBA: esto solo funciona si hay algún tipo de interrupción en el hilo de ejecución actual para permitir que las promesas se ejecuten ANTES de verificar las construcciones síncronas. Eso hace que esto tenga una utilidad más limitada de lo que inicialmente pensé, aunque sigue siendo útil para mi caso de uso (gracias por señalar esto)

/** * This function allow you to modify a JS Promise by adding some status properties. * Based on: http://.com/questions/21485545/is-there-a-way-to-tell-if-an-es6-promise-is-fulfilled-rejected-resolved * But modified according to the specs of promises : https://promisesaplus.com/ */ function MakeQuerablePromise(promise) { // Don''t modify any promise that has been already modified. if (promise.isFulfilled) return promise; // Set initial state var isPending = true; var isRejected = false; var isFulfilled = false; // Observe the promise, saving the fulfillment in a closure scope. var result = promise.then( function(v) { isFulfilled = true; isPending = false; return v; }, function(e) { isRejected = true; isPending = false; throw e; } ); result.isFulfilled = function() { return isFulfilled; }; result.isPending = function() { return isPending; }; result.isRejected = function() { return isRejected; }; return result; } wrappedPromise = MakeQueryablePromise(Promise.resolve(3)); setTimeout(function() {console.log(wrappedPromise.isFulfilled())}, 1);

Desde https://ourcodeworld.com/articles/read/317/how-to-check-if-a-javascript-promise-has-been-fulfilled-rejected-or-resolved que basaron su respuesta en ¿Hay alguna manera de saber si una promesa de ES6 se cumple / rechaza / resuelve?


Esta es una pregunta anterior, pero estaba tratando de hacer algo similar. Necesito mantener a n trabajadores trabajando. Están estructurados en una promesa. Necesito escanear y ver si están resueltos, rechazados o aún pendientes. Si se resuelve, necesito el valor, si se rechaza hacer algo para corregir el problema o pendiente. Si se resuelve o rechaza, necesito comenzar otra tarea para continuar. No puedo encontrar una manera de hacerlo con Promise.all o Promise.race, ya que sigo trabajando las promesas en una matriz y no puedo encontrar la manera de eliminarlas. Entonces creo un trabajador que hace el truco

Necesito una función de generador de promesas que devuelva una promesa que se resuelva o rechace según sea necesario. Es invocado por una función que configura el marco para saber qué está haciendo la promesa.

En el siguiente código, el generador simplemente devuelve una promesa basada en setTimeout.

Aquí está

//argObj should be of form // {succeed: <true or false, nTimer: <desired time out>} function promiseGenerator(argsObj) { let succeed = argsObj.succeed; let nTimer = argsObj.nTimer; return new Promise((resolve, reject) => { setTimeout(() => { if (succeed) { resolve(''ok''); } else { reject(`fail`); } }, nTimer); }) } function doWork(generatorargs) { let sp = { state: `pending`, value: ``, promise: "" }; let p1 = promiseGenerator(generatorargs) .then((value) => { sp.state = "resolved"; sp.value = value; }) .catch((err) => { sp.state = "rejected"; sp.value = err; }) sp.promise = p1; return sp; }

doWork devuelve un objeto que contiene la promesa y su estado y valor devuelto.

El siguiente código ejecuta un ciclo que prueba el estado y crea nuevos trabajadores para mantenerlo en 3 trabajadores en ejecución.

let promiseArray = []; promiseArray.push(doWork({ succeed: true, nTimer: 1000 })); promiseArray.push(doWork({ succeed: true, nTimer: 500 })); promiseArray.push(doWork({ succeed: false, nTimer: 3000 })); function loopTimerPromise(delay) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(''ok''); }, delay) }) } async function looper() { let nPromises = 3; //just for breaking loop let nloop = 0; //just for breaking loop let i; //let continueLoop = true; while (true) { await loopTimerPromise(900); //execute loop every 900ms nloop++; //console.log(`promiseArray.length = ${promiseArray.length}`); for (i = promiseArray.length; i--; i > -1) { console.log(`index ${i} state: ${promiseArray[i].state}`); switch (promiseArray[i].state) { case "pending": break; case "resolved": nPromises++; promiseArray.splice(i, 1); promiseArray.push(doWork({ succeed: true, nTimer: 1000 })); break; case "rejected": //take recovery action nPromises++; promiseArray.splice(i, 1); promiseArray.push(doWork({ succeed: false, nTimer: 500 })); break; default: console.log(`error bad state in i=${i} state:${promiseArray[i].state} `) break; } } console.log(``); if (nloop > 10 || nPromises > 10) { //should do a Promise.all on remaining promises to clean them up but not for test break; } } } looper();

Probado en node.js

Por cierto, no tanto en esta respuesta, sino en otros sobre temas similares, ODIO cuando alguien dice "no entiendes" o "no funciona así". Por lo general, supongo que el interlocutor sabe lo que quiere. Sugerir una mejor manera es genial. Una explicación paciente de cómo funcionan las promesas también sería buena.


He escrito un pequeño paquete npm, promise-value, que proporciona un contenedor de promesas con un indicador resolved :

https://www.npmjs.com/package/promise-value

También le da acceso sincrónico al valor prometido (o error). Esto no altera el objeto Promise en sí mismo, siguiendo el ajuste en lugar de extender el patrón.


No, no hay API de sincronización, pero aquí está mi versión de async promiseState (con ayuda de @Matthijs):

function promiseState(p) { const t = {}; return Promise.race([p, t]) .then(v => (v === t)? "pending" : "fulfilled", () => "rejected"); } var a = Promise.resolve(); var b = Promise.reject(); var c = new Promise(() => {}); promiseState(a).then(state => console.log(state)); // fulfilled promiseState(b).then(state => console.log(state)); // rejected promiseState(c).then(state => console.log(state)); // pending


Puede agregar un método a Promise.prototype. Se parece a esto:

Editado: la primera solución no funciona correctamente, como la mayoría de las respuestas aquí. Devuelve "pendiente" hasta que se invoca la función asincrónica ".then", lo que no ocurre inmediatamente. (Lo mismo se trata de soluciones que usan Promise.race). Mi segunda solución resuelve este problema.

if (window.Promise) { Promise.prototype.getState = function () { if (!this.state) { this.state = "pending"; var that = this; this.then( function (v) { that.state = "resolved"; return v; }, function (e) { that.state = "rejected"; return e; }); } return this.state; }; }

Puedes usarlo en cualquier Promesa. Por ejemplo:

myPromise = new Promise(myFunction); console.log(myPromise.getState()); // pending|resolved|rejected

Segunda (y correcta) solución:

if (window.Promise) { Promise.stateable = function (func) { var state = "pending"; var pending = true; var newPromise = new Promise(wrapper); newPromise.state = state; return newPromise; function wrapper(resolve, reject) { func(res, rej); function res(e) { resolve(e); if (pending) { if (newPromise) newPromise.state = "resolved"; else state = "resolved"; pending = false; } } function rej(e) { reject(e); if (pending) { if (newPromise) newPromise.state = "rejected"; else state = "rejected"; pending = false; } } } }; }

Y úsalo:

Aviso : en esta solución no tiene que usar el operador "nuevo".

myPromise = Promise.stateable(myFunction); console.log(myPromise.state); // pending|resolved|rejected


Puede usar un hack (feo) en Node.js hasta que se ofrezca un método nativo:

util = require(''util''); var promise1 = new Promise (function (resolve) { } var promise2 = new Promise (function (resolve) { resolve (''foo''); } state1 = util.inspect (promise1); state2 = util.inspect (promise2); if (state1 === ''Promise { <pending> }'') { console.log(''pending''); // pending } if (state2 === "Promise { ''foo'' }") { console.log (''foo'') // foo }


Puedes envolver tus promesas de esta manera

Promise.getInfo = function( arg ) { const pending = {}; let status, value; Promise.race([ arg, pending ]).then( x => { status = ''fulfilled''; value = x; }, x => { status = ''rejected''; value = x; } ); process._tickCallback(); // run microtasks right now if( value === pending ) return { status: ''pending'' }; return { status, value }; };


Puedes hacer una carrera con Promise.resolve
No es sincrónico pero sucede ahora

function promiseState(p, isPending, isResolved, isRejected) { Promise.race([p, Promise.resolve(''a value that p should not return'')]).then(function(value) { if (value == ''a value that p should not return'') { (typeof(isPending) === ''function'') && isPending(); }else { (typeof(isResolved) === ''function'') && isResolved(value); } }, function(reason) { (typeof(isRejected) === ''function'') && isRejected(reason); }); }

Un pequeño script para probar y comprender su significado de forma asincrónica

var startTime = Date.now() - 100000;//padding trick "100001".slice(1) => 00001 function log(msg) { console.log((""+(Date.now() - startTime)).slice(1) + '' '' + msg); return msg;//for chaining promises }; function prefix(pref) { return function (value) { log(pref + value); return value; };} function delay(ms) { return function (value) { var startTime = Date.now(); while(Date.now() - startTime < ms) {} return value;//for chaining promises }; } setTimeout(log, 0,''timeOut 0 ms''); setTimeout(log, 100,''timeOut 100 ms''); setTimeout(log, 200,''timeOut 200 ms''); var p1 = Promise.resolve(''One''); var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "Two"); }); var p3 = Promise.reject("Three"); p3.catch(delay(200)).then(delay(100)).then(prefix(''delayed L3 : '')); promiseState(p1, prefix(''p1 Is Pending ''), prefix(''p1 Is Resolved ''), prefix(''p1 Is Rejected '')); promiseState(p2, prefix(''p2 Is Pending ''), prefix(''p2 Is Resolved ''), prefix(''p2 Is Rejected '')); promiseState(p3, prefix(''p3 Is Pending ''), prefix(''p3 Is Resolved ''), prefix(''p3 Is Rejected '')); p1.then(prefix(''Level 1 : '')).then(prefix(''Level 2 : '')).then(prefix(''Level 3 : '')); p2.then(prefix(''Level 1 : '')).then(prefix(''Level 2 : '')).then(prefix(''Level 3 : '')); p3.catch(prefix(''Level 1 : '')).then(prefix(''Level 2 : '')).then(prefix(''Level 3 : '')); log(''end of promises''); delay(100)(); log(''end of script'');

resultados con retraso (0) (comentar el tiempo de retraso)

00001 end of promises 00001 end of script 00001 Level 1 : One 00001 Level 1 : Three 00001 p1 Is Resolved One 00001 p2 Is Pending undefined 00001 p3 Is Rejected Three 00001 Level 2 : One 00001 Level 2 : Three 00001 delayed L3 : Three 00002 Level 3 : One 00002 Level 3 : Three 00006 timeOut 0 ms 00100 timeOut 100 ms 00100 Level 1 : Two 00100 Level 2 : Two 00101 Level 3 : Two 00189 timeOut 200 ms

y los resultados de esta prueba con firefox (Chrome mantiene el orden)

00000 end of promises 00100 end of script 00300 Level 1 : One 00300 Level 1 : Three 00400 p1 Is Resolved One 00400 p2 Is Pending undefined 00400 p3 Is Rejected Three 00400 Level 2 : One 00400 Level 2 : Three 00400 delayed L3 : Three 00400 Level 3 : One 00400 Level 3 : Three 00406 timeOut 0 ms 00406 timeOut 100 ms 00406 timeOut 200 ms 00406 Level 1 : Two 00407 Level 2 : Two 00407 Level 3 : Two

promiseState make .race y .then: Level 2


Si usa ES7 experimental, puede usar async para ajustar fácilmente la promesa que desea escuchar.

async function getClient() { let client, resolved = false; try { client = await new Promise((resolve, reject) => { let client = new Client(); let timer = setTimeout(() => { reject(new Error(`timeout`, 1000)); client.close(); }); client.on(''ready'', () => { if(!resolved) { clearTimeout(timer); resolve(client); } }); client.on(''error'', (error) => { if(!resolved) { clearTimeout(timer); reject(error); } }); client.on(''close'', (hadError) => { if(!resolved && !hadError) { clearTimeout(timer); reject(new Error("close")); } }); }); resolved = true; } catch(error) { resolved = true; throw error; } return client; }


en el nodo, diga process.binding(''util'').getPromiseDetails(promise) interno no documentado process.binding(''util'').getPromiseDetails(promise)

> process.binding(''util'').getPromiseDetails(Promise.resolve({data: [1,2,3]})); [ 1, { data: [ 1, 2, 3 ] } ] > process.binding(''util'').getPromiseDetails(Promise.reject(new Error(''no''))); [ 2, Error: no ] > process.binding(''util'').getPromiseDetails(new Promise((resolve) => {})); [ 0, <1 empty item> ]


lo que puede hacer es usar una variable para almacenar el estado, establecer manualmente el estado en esa variable y verificar esa variable.

var state = ''pending''; new Promise(function(ff, rjc) { //do something async if () {//if success state = ''resolved''; ff();// } else { state = ''rejected''; rjc(); } }); console.log(state);//check the state somewhere else in the code

Por supuesto, esto significa que debe tener acceso al código original de la promesa. Si no lo hace, entonces puede hacer:

var state = ''pending''; //you can''t access somePromise''s code somePromise.then(function(){ state = ''resolved''; }, function() { state = ''rejected''; }) console.log(state);//check the promise''s state somewhere else in the code

Mi solución es más codificación, pero creo que probablemente no tenga que hacer esto para cada promesa que use.


promise-status-async hace el truco. Es asíncrono, pero no sirve para esperar la promesa que se resolverá.

const {promiseStatus} = require(''promise-status-async''); // ... if (await promiseStatus(promise) === ''pending'') { const idle = new Promise(function(resolve) { // can do some IDLE job meanwhile }); return idle; }


await uso de la respuesta de @ jib , con prototipos idiomáticos.

Object.defineProperty(Promise.prototype, "state", { get: function(){ const o = {}; return Promise.race([this, o]).then( v => v === o ? "pending" : "resolved", () => "rejected"); } }); // usage: (async () => { console.log(await <Your Promise>.state); console.log(await Promise.resolve(2).state); // "resolved" console.log(await Promise.reject(0).state); // "rejected" console.log(await new Promise(()=>{}).state); // "pending" });

tenga en cuenta que esta función asíncrona se ejecuta "casi" inmediatamente como una función sincronizada (o en realidad puede ser instantáneamente).


No existe tal API de inspección síncrona para las promesas nativas de JavaScript. Es imposible hacer esto con las promesas nativas. La especificación no especifica dicho método.

Las bibliotecas Userland pueden hacer esto, y si está apuntando a un motor específico (como v8) y tiene acceso al código de la plataforma (es decir, puede escribir código en el núcleo ), puede usar herramientas específicas (como símbolos privados) para lograr esto . Sin embargo, eso es súper específico y no en el área de usuarios.