Ya sea una promesa ES6 o una promesa Bluebird, Q Promise, etc.
¿Cómo pruebo para ver si un objeto dado es una promesa?
fuente
Ya sea una promesa ES6 o una promesa Bluebird, Q Promise, etc.
¿Cómo pruebo para ver si un objeto dado es una promesa?
Si tiene una .then
función, esa es la única promesa estándar que usan las bibliotecas.
La especificación Promesas / A + tiene una noción llamada then
capaz que es básicamente "un objeto con un then
método". Las promesas harán y deberían asimilar cualquier cosa con un método de entonces. Toda la implementación de promesa que ha mencionado hace esto.
Si nos fijamos en la especificación :
2.3.3.3 si
then
es una función, llámela con x como esto, primer argumento resolvePromise y segundo argumento acceptPromise
También explica la justificación de esta decisión de diseño:
Este tratamiento de
then
ables permite que las implementaciones de promesas interoperen, siempre que expongan unthen
método compatible con Promesas / A + . También permite que las implementaciones de Promises / A + "asimilen" implementaciones no conformes con métodos razonables.
No debería, en su lugar, llamar Promise.resolve(x)
( Q(x)
en Q) que siempre convertirá cualquier valor o then
capacidad externa en una promesa confiable. Es más seguro y fácil que realizar estas comprobaciones usted mismo.
Siempre puede ejecutarlo a través del conjunto de pruebas : D
Comprobar si algo es prometedor complica innecesariamente el código, solo use Promise.resolve
Promise.resolve(valueOrPromiseItDoesntMatter).then(function(value) {
})
Aquí está mi respuesta original, que desde entonces ha sido ratificada en la especificación como la forma de probar una promesa:
Promise.resolve(obj) == obj
Esto funciona porque el algoritmo exige explícitamente que Promise.resolve
debe devolver el objeto exacto pasado si y solo si es una promesa según la definición de la especificación.
Tengo otra respuesta aquí, que solía decir esto, pero la cambié a otra cuando no funcionaba con Safari en ese momento. Eso fue hace un año, y ahora funciona de manera confiable incluso en Safari.
Habría editado mi respuesta original, excepto que se sintió mal, dado que ahora más personas han votado por la solución alterada en esa respuesta que la original. Creo que esta es la mejor respuesta, y espero que esté de acuerdo.
===
lugar de ==
?
Actualización: esta ya no es la mejor respuesta. Por favor vote mi otra respuesta en su lugar.
obj instanceof Promise
Deberías hacerlo. Tenga en cuenta que esto solo puede funcionar de manera confiable con las promesas nativas de es6.
Si está utilizando una cuña, una biblioteca de promesas o cualquier otra cosa que pretenda ser como una promesa, entonces puede ser más apropiado hacer una prueba para un "thenable" (cualquier cosa con un .then
método), como se muestra en otras respuestas aquí.
Promise.resolve(obj) == obj
no funcionará en Safari. Usar en su instanceof Promise
lugar.
obj && typeof obj.then == 'function'
, porque funcionará con todo tipo de promesas y en realidad es la forma recomendada por la especificación y utilizada por las implementaciones / polyfills. Native, Promise.all
por ejemplo, funcionará en todas las then
capacidades, no solo en otras promesas nativas. También debería tu código. Entonces instanceof Promise
no es una buena solución.
console.log(typeof p, p, p instanceof Promise);
produce este resultado: object Promise { <pending> } false
. Como puede ver, es una promesa, ¿y la instanceof Promise
prueba vuelve false
?
if (typeof thing.then === 'function') {
// probably a promise
} else {
// definitely not a promise
}
Para ver si el objeto dado es una promesa ES6 , podemos hacer uso de este predicado:
function isPromise(p) {
return p && Object.prototype.toString.call(p) === "[object Promise]";
}
Call
ing toString
directamente del Object.prototype
devuelve una representación de cadena nativa del tipo de objeto dado que es "[object Promise]"
en nuestro caso. Esto asegura que el objeto dado
toString
Método auto-escrito del objeto dado.instanceof
o isPrototypeOf
.Sin embargo, cualquier objeto host particular , que tenga su etiqueta modificada a través deSymbol.toStringTag
, puede regresar "[object Promise]"
. Este puede ser el resultado deseado o no dependiendo del proyecto (por ejemplo, si hay una implementación personalizada de Promise).
Para ver si el objeto es de una Promesa ES6 nativa , podemos usar:
function isNativePromise(p) {
return p && typeof p.constructor === "function"
&& Function.prototype.toString.call(p.constructor).replace(/\(.*\)/, "()")
=== Function.prototype.toString.call(/*native object*/Function)
.replace("Function", "Promise") // replacing Identifier
.replace(/\(.*\)/, "()"); // removing possible FormalParameterList
}
De acuerdo con esta y esta sección de la especificación, la representación de cadena de la función debe ser:
" Identificador de función ( FormalParameterList opt ) { FunctionBody }"
que se maneja en consecuencia arriba. El FunctionBody es [native code]
en todos los principales navegadores.
MDN: Function.prototype.toString
Esto también funciona en múltiples contextos de entorno.
No es una respuesta a la pregunta completa, pero creo que vale la pena mencionar que en Node.js 10 isPromise
se agregó una nueva función de utilidad llamada que comprueba si un objeto es una Promesa nativa o no:
const utilTypes = require('util').types
const b_Promise = require('bluebird')
utilTypes.isPromise(Promise.resolve(5)) // true
utilTypes.isPromise(b_Promise.resolve(5)) // false
Así es como el paquete graphql-js detecta las promesas:
function isPromise(value) {
return Boolean(value && typeof value.then === 'function');
}
value
es el valor devuelto de su función. Estoy usando este código en mi proyecto y no tengo ningún problema hasta ahora.
Aquí está el formulario de código https://github.com/ssnau/xkit/blob/master/util/is-promise.js
!!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
si un objeto con un then
método, debe tratarse como a Promise
.
En caso de que esté utilizando Typecript , me gustaría agregar que puede usar la función "predicado de tipo". Solo debe envolver la verificación lógica en una función que regrese x is Promise<any>
y no necesitará hacer typecasts. A continuación, en mi ejemplo, c
hay una promesa o uno de mis tipos que quiero convertir en una promesa llamando al c.fetch()
método.
export function toPromise(c: Container<any> | Promise<any>): Promise<any> {
if (c == null) return Promise.resolve();
return isContainer(c) ? c.fetch() : c;
}
export function isContainer(val: Container<any> | Promise<any>): val is Container<any> {
return val && (<Container<any>>val).fetch !== undefined;
}
export function isPromise(val: Container<any> | Promise<any>): val is Promise<any> {
return val && (<Promise<any>>val).then !== undefined;
}
Más información: https://www.typescriptlang.org/docs/handbook/advanced-types.html
Si utiliza un método asíncrono, puede hacerlo y evitar cualquier ambigüedad.
async myMethod(promiseOrNot){
const theValue = await promiseOrNot()
}
Si la función devuelve la promesa, esperará y volverá con el valor resuelto. Si la función devuelve un valor, se tratará como resuelto.
Si la función no devuelve una promesa hoy, pero mañana devuelve una o se declara asíncrona, estará preparado para el futuro.
Promise.resolve()
it('should return a promise', function() {
var result = testedFunctionThatReturnsPromise();
expect(result).toBeDefined();
// 3 slightly different ways of verifying a promise
expect(typeof result.then).toBe('function');
expect(result instanceof Promise).toBe(true);
expect(result).toBe(Promise.resolve(result));
});
Yo uso esta función como una solución universal:
function isPromise(value) {
return value && value.then && typeof value.then === 'function';
}
Después de buscar una forma confiable de detectar funciones asincrónicas o incluso promesas , terminé usando la siguiente prueba:
() => fn.constructor.name === 'Promise' || fn.constructor.name === 'AsyncFunction'
Promise
y crea instancias de eso, esta prueba puede fallar. Sin embargo, esto debería funcionar para la mayoría de lo que estás tratando de probar.
fn.constructor.name === 'AsyncFunction'
está mal, significa que algo es una función asincrónica y no una promesa, tampoco se garantiza que funcione porque la gente puede subclasificar las promesas
ES6:
const promise = new Promise(resolve => resolve('olá'));
console.log(promise.toString().includes('Promise')); //true
toString
método puede devolver una cadena que incluya "Promise"
.
'NotAPromise'.toString().includes('Promise') === true
.then
método, pero eso no le diría que lo que tiene es una Promesa definitivamente. Todo lo que sabría en ese momento es que tiene algo que expone un.then
método, como una Promesa..then
método que no sea una Promesa, no se comporte como una Promesa y no tuviera la intención de ser utilizado como una Promesa. Buscar un.then
método solo te dice que si el objeto no tiene un.then
método, entonces no tienes una Promesa. La inversa - que la existencia de unos.then
medios método que haces tiene una promesa - no es necesariamente cierto..then
método. Sí, eso tiene el potencial de falsos positivos, pero se supone que todas las bibliotecas prometedoras dependen (porque eso es todo en lo que pueden confiar). La única alternativa hasta donde puedo ver es tomar la sugerencia de Benjamin Gruenbaum y ejecutarla a través del conjunto de pruebas de promesa. Pero eso no es práctico para el código de producción real.