我如何判断一个对象是否是 Promise?

How do I tell if an object is a Promise?

无论是ES6 Promise还是bluebird Promise、Q Promise等

如何测试给定对象是否为 Promise?

promise 库如何决定

如果它具有 .then 函数 - 那是 标准承诺库使用。

Promises/A+ 规范有一个名为 thenable 的概念,它基本上是“具有 then 方法的对象”。 Promises 将并且应该使用 then 方法吸收 anything。您提到的所有 promise 实现都是这样做的。

如果我们看 specification:

2.3.3.3 if then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise

它还解释了此设计决策的基本原理:

This treatment of thenables allows promise implementations to interoperate, as long as they expose a Promises/A+-compliant then method. It also allows Promises/A+ implementations to “assimilate” nonconformant implementations with reasonable then methods.

你应该如何决定

您不应该 - 而是调用 Promise.resolve(x)(Q 中的 Q(x)),它将 总是 转换任何值或外部 thenable变成可信赖的承诺。比自己进行这些检查更安全、更容易。

真的需要确定吗?

你总是可以 运行 通过 the test suite :D

if (typeof thing?.then === 'function') {
    // probably a promise
} else {
    // definitely not a promise
}

检查是否是 promise 会不必要地使代码复杂化,只需使用 Promise.resolve

Promise.resolve(valueOrPromiseItDoesntMatter).then(function(value) {

})

免责声明:不是更新 OP 的好答案,仅适用于本机,不适用于跨领域。改为关注

obj instanceof Promise

应该这样做。请注意,这可能仅适用于原生 es6 承诺。

如果您使用填充程序、promise 库或任何其他伪装成类似 promise 的东西,那么测试“thenable”(任何具有 .then 方法的东西)可能更合适,如此处的其他答案所示。

这里是代码形式https://github.com/ssnau/xkit/blob/master/util/is-promise.js

!!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';

如果对象具有 then 方法,则应将其视为 Promise

免责声明:不是更新 OP 的好答案,它是针对每个库的,不会跨领域工作。请检查 .then

这个答案 based on the spec 是一种测试仅在某些时候有效的承诺的方法,仅供参考。

Promise.resolve(obj) == obj &&
BLUEBIRD.resolve(obj) == obj

这是因为 algorithm 明确要求 Promise.resolve 必须 return 传入的确切对象 if 并且 仅当 这是一个由这个构造函数创建的promise。

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));
});

要查看给定对象是否是 ES6 Promise,我们可以使用这个谓词:

function isPromise(p) {
  return p && Object.prototype.toString.call(p) === "[object Promise]";
}

Calling toString directly from the Object.prototype returns a native string representation 给定的对象类型,在我们的例子中是 "[object Promise]"。这确保了给定的对象

  • 绕过误报,例如..:
    • 具有相同构造函数名称的自定义对象类型("Promise")。
    • 给定对象的自写toString方法
  • 适用于多个环境上下文(例如 iframe)in contrast to instanceofisPrototypeOf

但是,任何特定的 host object, that has its tag modified via Symbol.toStringTag,都可以 return "[object Promise]"。这可能是预期的结果,也可能不是预期的结果,具体取决于项目(例如,如果有自定义的 Promise 实现)。


要查看对象是否来自 原生 ES6 Promise,我们可以使用:

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 
}

根据规范的this and this section,函数的字符串表示应该是:

"function Identifier ( FormalParameterListopt ) { FunctionBody }"

上面相应处理。 FunctionBody 在所有主流浏览器中都是 [native code]

MDN: Function.prototype.toString

这也适用于多个环境上下文。

ES6:

const promise = new Promise(resolve => resolve('olá'));

console.log(promise.toString().includes('Promise')); //true

如果您使用的是 Typescript,我想补充一点,您可以使用 "type predicate" 功能。只需将逻辑验证包装在 returns x is Promise<any> 的函数中,您就不需要进行类型转换。在我的示例下方,c 是一个承诺或我想通过调用 c.fetch() 方法将其转换为承诺的类型之一。

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;
}

更多信息:https://www.typescriptlang.org/docs/handbook/advanced-types.html

不是完整问题的答案,但我认为值得一提的是,在 Node.js 10 中添加了一个名为 isPromise 的新实用函数,用于检查对象是否为原生 Promise :

const utilTypes = require('util').types
const b_Promise = require('bluebird')

utilTypes.isPromise(Promise.resolve(5)) // true
utilTypes.isPromise(b_Promise.resolve(5)) // false

在搜索 可靠 方法来检测 Async 函数甚至 Promises 之后,我最终使用了以下测试:

() => fn.constructor.name === 'Promise' || fn.constructor.name === 'AsyncFunction'

如果您在异步方法中,您可以这样做并避免任何歧义。

async myMethod(promiseOrNot){
  const theValue = await promiseOrNot()
}

如果函数 return 承诺,它将等待并 return 解析值。如果函数 return 是一个值,它将被视为已解决。

如果函数今天不 return 承诺,但明天 return 承诺或被声明为异步,你将面向未来。

这就是 graphql-js 包检测承诺的方式:

function isPromise(value) {
  return Boolean(value && typeof value.then === 'function');
}

value 是函数的返回值。我在我的项目中使用这段代码,到目前为止没有问题。

我用这个功能作为通用解决方案:

function isPromise(value) {
  return value && value.then && typeof value.then === 'function';
}
const isPromise = (value) => {
  return !!(
    value &&
    value.then &&
    typeof value.then === 'function' &&
    value?.constructor?.name === 'Promise'
  )
}

我这个检查比较好,试试看

为了避免比较而将可能的同步 value 推入 Promise.resolve(value) 的任何事情都会将您的代码变成本来可以避免的异步。有时您在那个阶段不想要它。您想知道在微任务队列中的一些较早的解决方案对您产生影响之前评估的结果吗?

一个人可能会喜欢;

var isPromise = x => Object(x).constructor === Promise;

我根据我能想到的一些极端情况检查了它,它似乎有效。

isPromise(undefined);                                           // <- false
isPromise(null);                                                // <- false
isPromise(0);                                                   // <- false
isPromise("");                                                  // <- false
isPromise({});                                                  // <- false
isPromise(setTimeout);                                          // <- false
isPromise(Promise);                                             // <- false
isPromise(new Promise((v,x) => setTimeout(v,1000,"whatever"))); // <- true
isPromise(fetch('http://example.com/movies.json'));             // <- true

我还没有对照任何非本地库对其进行检查,但现在有什么意义呢?

在Angular中:

import { isPromise } from '@angular/compiler/src/util';

if (isPromise(variable)) {
  // do something
}

J

使用这个库

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

import isPromise from 'is-promise';
 
isPromise(Promise.resolve());//=>true
isPromise({then:function () {...}});//=>true
isPromise(null);//=>false
isPromise({});//=>false
isPromise({then: true})//=>false