【问题标题】:How do I tell if an object is a Promise?如何判断一个对象是否是 Promise?
【发布时间】:2015-03-01 00:42:38
【问题描述】:

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

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

【问题讨论】:

  • 您充其量可以检查.then 方法,但这并不能告诉您您所拥有的是明确的Promise。那时你所知道的就是你有一些东西暴露了一个.then 方法,like一个 Promise。
  • @ScottOffen 承诺规范明确地没有区分。
  • 我的观点是,任何人都可以创建一个对象来公开.then 方法,该方法不是 Promise,它的行为不像 Promise,也不打算像 Promise 那样使用。检查.then 方法只是告诉你如果对象没有.then 方法,那么你没有 有一个Promise。反过来——.then 方法的存在意味着你确实有一个 Promise——不一定是真的。
  • @ScottOffen 根据定义,识别 Promise 的唯一既定方法是检查它是否具有 .then 方法。是的,这有可能导致误报,但这是所有 Promise 库都依赖的假设(因为这是它们可以依赖的全部)。据我所知,唯一的选择是采纳 Benjamin Gruenbaum 的建议并通过 promise 测试套件运行它。但这对于实际的生产代码是不切实际的。
  • const isPromise = v => typeof v === 'object' && typeof v.then === 'function'

标签: javascript promise q bluebird es6-promise


【解决方案1】:

promise 库如何决定

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

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

如果我们查看specification

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

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

这种对thenables 的处理允许promise 实现互操作,只要它们公开符合Promises/A+ 的then 方法。它还允许 Promises/A+ 实现使用合理的 then 方法“吸收”不符合要求的实现。

你应该如何决定

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

真的需要确定吗?

您始终可以通过the test suite 运行它:D

【讨论】:

  • 执行 Promise.resolve(x)(Q 中的 Q(x))如何告诉你 x 是否是一个 Promise?
【解决方案2】:

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

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

})

【讨论】:

  • @AlexMills 是的,它甚至适用于非标准的承诺,如 jQuery 承诺。如果对象的 then 方法的接口与 promise then 完全不同,则它可能会失败。
  • 这个答案虽然可能是个好建议,但实际上并没有回答这个问题。
  • 除非问题真的是关于某人实际实现了一个承诺库,否则这个问题是无效的。只有 Promise 库需要进行检查,之后您可以随时使用它的 .resolve 方法,就像我展示的那样。
  • @Esalija 这个问题在我看来是相关且重要的,而不仅仅是对 promise 库的实现者而言。它还与希望了解实现将/应该/可能如何表现以及不同的 Promise 库如何相互交互的 Promise 库的用户 相关。特别是,这个用户非常沮丧,因为我可以为任何 X 做出 X 的承诺,除非 X 是“承诺”(无论“承诺”在这里是什么意思——这就是问题所在),我绝对感兴趣确切地知道该异常的边界在哪里。
  • @Esailija 我有一个变量来指示代码是否已加载。这个变量可能是一个承诺,表明它正在加载,我应该等待,或者是一个表明它已加载的值,这样我就可以正确地渲染它。值得一提的是,我将渲染一个加载动画,如果它仍在加载,这将非常复杂。因此,无论如何我都不能等待,因为如果我总是等待,将调用 render() 并使用每次都未加载的代码并创建整个加载动画,即使它已经准备好。
【解决方案3】:

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

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

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

这是因为algorithm 明确要求Promise.resolve 必须返回传入的确切对象,如果并且仅当这是一个承诺由这个构造函数创建

【讨论】:

  • 你应该使用===而不是==吗?
  • @NeilS == is fine.
  • 这对于不属于同一领域的承诺也会失败。
  • “规范定义的承诺”似乎是指“由相同构造函数创建的承诺与通过 Promise.resolve() 创建的承诺将是” - 所以这将无法检测到如果例如。一个 polyfill 的 Promise 实际上是一个 Promise
  • 如果从说明您如何解释问题而不是立即从答案开始,则可以改进此答案-不幸的是,OP根本没有说清楚,而且您还没有也不是,所以此时 OP、作者和读者可能在 3 个不同的页面上。您引用的文档说“如果参数是一个承诺由这个构造函数产生”,斜体部分至关重要。最好声明这就是您要回答的问题。此外,您的回答对这个库的用户有用,但对实现者没有用。
【解决方案4】:

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

obj instanceof Promise

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

如果您正在使用 shim、promise 库或任何其他伪装成类似 promise 的东西,那么测试“thenable”(任何带有.then 方法的东西)可能更合适,如其他答案在这里。

【讨论】:

  • 自从been pointed out to me 之后,Promise.resolve(obj) == obj 就无法在 Safari 中工作了。请改用instanceof Promise
  • 这不能可靠地工作,并导致我非常难以跟踪问题。假设您有一个使用 es6.promise shim 的库,并且您在某处使用 Bluebird,您会遇到问题。我在 Chrome Canary 中遇到了这个问题。
  • 是的,这个答案实际上是错误的。我最终来到这里是为了解决一个如此难以追踪的问题。你真的应该检查obj && typeof obj.then == 'function',因为它适用于所有类型的promise,并且实际上是规范推荐和实现/polyfills使用的方式。例如,本机 Promise.all 将适用于所有 thenables,而不仅仅是其他本机承诺。你的代码也应该如此。所以instanceof Promise 不是一个好的解决方案。
  • 跟进 - 更糟糕的是:在 node.js 6.2.2 上仅使用原生承诺我现在正在尝试调试console.log(typeof p, p, p instanceof Promise); 产生此输出的问题:object Promise { <pending> } false。如您所见,这是一个很好的承诺 - 但是 instanceof Promise 测试返回 false?
  • 这对于不属于同一领域的承诺将失败。
【解决方案5】:
if (typeof thing?.then === 'function') {
    // probably a promise
} else {
    // definitely not a promise
}

【讨论】:

  • 如果事物未定义怎么办?你需要通过 thing && ... 来防范这种情况
  • 不是最好的,但绝对很有可能;还取决于问题的范围。 100% 防御性编写通常适用于开放式公共 API,或者您知道数据的形状/签名是完全开放式的。
  • @mrBorna thing?.then 中的 ? 处理未定义的检查。这称为“可选链接”。阅读更多:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • if (p && 'then' in p && typeof p.then === 'function')
【解决方案6】:

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

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

Calling toString 直接从Object.prototype 返回给定对象类型的native string representation,在我们的例子中是"[object Promise]"。这确保了给定的对象

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

但是,任何具有tag modified via Symbol.toStringTag 的特定host object 都可以返回"[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 
}

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

“函数标识符FormalParameterListopt){FunctionBody}”

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

MDN: Function.prototype.toString

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

【讨论】:

    【解决方案7】:

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

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

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

    【讨论】:

      【解决方案8】:

      不是完整问题的答案,但我认为值得一提的是,在 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
      

      【讨论】:

      • 这应该是公认的答案。 @theram
      【解决方案9】:

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

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

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

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

      【讨论】:

      • 这有效,根据here:“如果 [awaited] 值不是一个承诺,[await 表达式] 将该值转换为已解决的 Promise,并等待它”
      • 这基本上是在接受的答案中所建议的,除了这里使用 async-await 语法而不是 Promise.resolve()
      【解决方案10】:

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

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

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

      【讨论】:

      • 为什么我们需要 obj === 'function' 条件顺便说一句?
      • this answer,任何对象都可以有一个“then”方法,因此不能总是被视为promise。
      【解决方案11】:

      如果您使用的是 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

      【讨论】:

        【解决方案12】:

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

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

        【讨论】:

        • 如果你继承 Promise 并创建它的实例,这个测试可能会失败。这应该适用于您尝试测试的大部分内容。
        • 同意,但我不明白为什么有人会创建承诺的子类
        • fn.constructor.name === 'AsyncFunction' 是错误的 - 这意味着某些东西是异步函数而不是承诺 - 而且它不能保证工作,因为人们可以继承承诺
        • @BenjaminGruenbaum 上面的示例在大多数情况下都有效,如果您创建自己的子类,则应在其名称上添加测试
        • 你可以,但是如果你已经知道有哪些对象,你就已经知道东西是否是承诺。
        【解决方案13】:

        为了避免比较而将可能的同步 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
        

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

        【讨论】:

          【解决方案14】:
          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));
          });
          

          【讨论】:

            【解决方案15】:

            我使用这个函数作为一个通用的解决方案:

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

            【讨论】:

              【解决方案16】:
              const isPromise = (value) => {
                return !!(
                  value &&
                  value.then &&
                  typeof value.then === 'function' &&
                  value?.constructor?.name === 'Promise'
                )
              }
              

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

              【讨论】:

                【解决方案17】:

                在 Angular 中:

                import { isPromise } from '@angular/compiler/src/util';
                
                if (isPromise(variable)) {
                  // do something
                }
                

                J

                【讨论】:

                  【解决方案18】:

                  使用这个库

                  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
                  

                  【讨论】:

                    【解决方案19】:

                    ES6:

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

                    【讨论】:

                    • 任何具有(或覆盖)toString 方法的对象都可以只返回一个包含"Promise" 的字符串。
                    • 这个答案不好有很多原因,最明显的是'NotAPromise'.toString().includes('Promise') === true
                    猜你喜欢
                    • 2012-10-16
                    • 1970-01-01
                    • 2011-11-28
                    • 1970-01-01
                    • 2018-05-06
                    • 2014-04-28
                    • 1970-01-01
                    • 2012-12-03
                    相关资源
                    最近更新 更多