如何检查对象是否为承诺

How to check if an object is a Promise?

本文关键字:对象 是否 承诺 检查 何检查      更新时间:2023-09-26

无论是ES6 Promise还是蓝鸟Promise,Q Promise等。

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

承诺库如何决定

如果它有一个.then函数 - 这是使用的唯一标准承诺。

Promises/A+ 规范有一个叫做 then able 的概念,它基本上是"具有then方法的对象"。承诺将而且应该用当时的方法吸收任何东西。你提到的所有承诺实现都是这样做的。

如果我们看一下规范:

2.3.3.3 如果then是一个函数,则用x调用它,第一个参数resolvePromise,第二个参数rejectPromise

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

这种对then能力的处理允许承诺实现进行互操作,只要它们公开符合 Promises/A+ 的 then 方法。它还允许 Promises/A+ 实现使用合理的 then 方法"吸收"不符合要求的实现。

你应该如何决定

你不应该 - 而是调用Promise.resolve(x)Q(x)在Q中),它将始终将任何值或外部then转换为可信的承诺。这比自己执行这些检查更安全、更容易。

真的需要确定吗?

您始终可以通过测试套件运行它:D

检查某些内容是否不必要地使代码复杂化,只需使用Promise.resolve

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

免责声明:更新的OP不是一个好的答案,是每个库的,并且不能跨领域工作。请改为检查.then

仅供参考,基于规范的这个答案是一种测试仅在有时有效的承诺的方法。

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

当这起作用时,这是因为算法明确要求Promise.resolve必须返回传入的确切对象,当且仅当它是此构造函数创建的承诺

免责声明:不是更新 OP 的好答案,仅适用于本机,不适用于跨领域。请改为遵循接受的答案。

obj instanceof Promise

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

如果您使用的是垫片、promise 库或其他任何假装类似 promise 的东西,那么测试"thenable"(任何具有.then方法的方法)可能更合适,如此处其他答案所示。

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

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

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

直接从Object.prototype Call toString返回给定对象类型的本机字符串表示形式,在我们的例子中"[object Promise]"。这可确保给定的对象

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

但是,任何通过 Symbol.toStringTag 修改其标记的特定主机对象都可以返回 "[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 
}

根据规范的这一部分和这一部分,函数的字符串表示应该是:

"function IdentifierFormalParameterListopt ) { FunctionBody }"

上面对此进行了相应的处理。FunctionBody 在所有主流浏览器中都[native code]

MDN: Function.prototype.toString

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

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

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

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

不是完整问题的答案,但我认为值得一提的是,在 Node.js 10 中添加了一个名为 isPromise 的新 util 函数,用于检查对象是否是本机 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 myMethod(promiseOrNot){
  const theValue = await promiseOrNot()
}

如果函数返回 promise,它将等待并返回解析的值。 如果函数返回一个值,它将被视为已解析。

如果函数今天没有返回承诺,但明天返回一个或被声明为异步,那么您将是面向未来的。

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

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

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

如果您使用的是 Typescript,我想补充一点,您可以使用"类型谓词"功能。只需将逻辑验证包装在返回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

在寻找一种可靠的方法来检测异步函数甚至 Promise 之后,我最终使用了以下测试:

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

任何将可能的同步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

我还没有检查过任何非本地图书馆,但现在有什么意义?

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

我使用此函数作为通用解决方案:

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

至于我 - 这个检查更好,试试看

对于那些尝试在 Typescript 中执行此操作的人 - 其他提供的解决方案存在哪些错误:

if (p instanceof Object && 'then' in p && typeof p.then === 'function') { ... }

使用此库

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

ES6:

const promise = new Promise(resolve => resolve('olá'));
console.log(promise.toString().includes('Promise')); //true