javascript 如何判断一个对象是否是 jQuery Promise/Deferred?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13075592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 17:46:10  来源:igfitidea点击:

How can I tell if an object is a jQuery Promise/Deferred?

javascriptjqueryjquery-deferred

提问by Facebook Staff are Complicit

I have a function that takes a single argument. I need to be able to tell if this argument is a jQuery Promiseor Deferredobject. If not, then the value may be of any type and have any properties, so it's not safe to just just for the presence of the promise methods.

我有一个接受单个参数的函数。我需要能够判断这个参数是 jQueryPromise还是Deferred对象。如果不是,则该值可能是任何类型并具有任何属性,因此仅针对 promise 方法的存在是不安全的。

Here's an example of how I'd like my function to behave:

这是我希望我的函数如何表现的示例:

function displayMessage(message) {
  if (message is a Promise or Deferred) {
    message.then(displayMessage);
  } else {
    alert(message);
  }
}

Notice the recursive handling of promises: if a promise is resolved with another promise value we don't display it, we wait for it to be resolved. If it returns yet another promise, repeat.

注意promise的递归处理:如果一个promise被另一个promise值解析,我们不显示它,我们等待它被解析。如果它返回另一个承诺,重复。



This is important because if this were not the case, I would just be able to use jQuery.when:

这很重要,因为如果不是这种情况,我将只能使用jQuery.when

function displayMessage(message) {
  jQuery.when(message).then(function(messageString) {
    alert(messageString);
  });
}

This would handle values and promises of values correctly...

这将正确处理值和值的承诺......

displayMessage("hello");                            // alerts "hello"
displayMessage(jQuery.Deferred().resolve("hello")); // alerts "hello"

...but once we get to promises of promises of values, it breaks down:

...但是一旦我们得到了对价值承诺的承诺,它就会崩溃:

displayMessage(jQuery.Deferred().resolve(
  jQuery.Deferred().resolve("hello")
));                                                 // alerts "[object Object]"


jQuery.whenis able to tell if a value is promise, so apparently it is possible. How can I check?

jQuery.when能够判断一个值是否是承诺,所以显然这是可能的。我该如何检查?

回答by Facebook Staff are Complicit

jQuery.whenis able to tell if a value is promise, so apparently it is possible.

jQuery.when能够判断一个值是否是承诺,所以显然这是可能的。

This is mistaken. jQuery itself is not able to check if an object is a promise with complete accuracy. If you look at the source of jQuery.whenin the jQuery source vieweryou can see that all it does is this:

这是错误的。jQuery 本身无法完全准确地检查对象是否是承诺。如果您查看jQuery.whenjQuery 源代码查看器中的源代码,您可以看到它所做的就是:

jQuery.isFunction(firstParam.promise)

If the object you are returning has its own .promise()method, jQuery.whenwill misbehave:

如果您要返回的对象有自己的.promise()方法,jQuery.when则会行为不端:

var trickyValue = {
  promise: function() { return 3; },
  value: 2
};

jQuery.when(trickyValue).then(function(obj) {
  alert(obj.value);
});

This throws TypeError: Object 3 has no method 'then', because jQuery assumes the object is a promise and trusts the value of its .promise()method.

这会抛出TypeError: Object 3 has no method 'then',因为 jQuery 假设该对象是一个 Promise 并且信任其.promise()方法的值。

This is probably impossible to solve properly. The promise object is created as an object literal inside of jQuery.Deferred(view source). It has no prototype, nor any other truly unique properties that could be used to distinguish it.

这可能是无法正确解决的。承诺对象被创建为jQuery.Deferred查看源代码)内部的对象字面量。它没有原型,也没有任何其他真正独特的属性可以用来区分它。

However, I can think of a hacky solution that should be reliable as long as only one version of jQuery is in use:

但是,我可以想到一个 hacky 解决方案,只要只使用一个版本的 jQuery,它就应该是可靠的:

function isPromise(value) {
  if (typeof value === 'object' && typeof value.then !== "function") {
    return false;
  }
  var promiseThenSrc = String($.Deferred().then);
  var valueThenSrc = String(value.then);
  return promiseThenSrc === valueThenSrc;
}

isPromise("test");                 // false
isPromise($.Deferred());           // true
isPromise($.Deferred().promise()); // true

Converting a function to a string gives you its source code, so here I am comparing then source of the .thenmethod of a new Deferredobject to that of the value I'm interested in. Your value is not going to have a .thenmethod with exactly the same source code as a jQuery.Deferredor Promise1.

将函数转换为字符串会为您提供其源代码,所以在这里我将.thenDeferred对象的方法的源与我感兴趣的值的源进行比较。您的值不会.then有完全相同的方法源代码为 ajQuery.DeferredPromise1

1.Unless you're running in a hostile environment, in which case you should probably give up.

1.除非您在恶劣的环境中运行,否则您可能应该放弃。



If you aren't specifically interested in jQuery promises, but would like to detect anytype of Promise including the built-in ones from ECMAScript 6, you can test if value is an object and has a thenmethod:

如果您对 jQuery Promise不是特别感兴趣,但想要检测任何类型的 Promise,包括 ECMAScript 6 中的内置 Promise,您可以测试 value 是否是一个对象并具有一个then方法:

if (typeof value === 'object' && typeof value.then === 'function') {
  // handle a promise
} else {
  // handle a concrete value
}

This is the approach by several Promise-handling functions defined in ES6. You can see this described in the specification of the resolve(...)functions, partially quoted below:

这是 ES6 中定义的几个 Promise 处理函数的方法。您可以resolve(...)功能规范中看到这一点,部分引用如下:

When a promise resolve function Fis called with argument resolution, the following steps are taken:

[...]

  1. If Type(resolution) is not Object, then
    1. Return FulfillPromise(promise, resolution).
  2. Let then be Get(resolution, "then").
  3. If then is an abrupt completion, then
    1. Return RejectPromise(promise, then.[[value]]).
  4. Let thenActionbe then.[[value]].
  5. If IsCallable(thenAction) is false, then
    1. Return FulfillPromise(promise, resolution).
  6. Perform EnqueueJob ("PromiseJobs", PromiseResolveThenableJob, ??promise, resolution, thenAction?)

当使用参数resolution调用promise 解析函数F 时,将执行以下步骤:

[...]

  1. 如果 Type( resolution) 不是 Object,则
    1. 返回 FulfillPromise( promise, resolution)。
  2. 令为 Get( resolution, "then")。
  3. 如果 then 是一个突然的完成,那么
    1. 返回 RejectPromise( promise, then.[[value]])。
  4. thenActionthen.[[value]]。
  5. 如果 IsCallable( thenAction) 为false,则
    1. 返回 FulfillPromise( promise, resolution)。
  6. 执行 EnqueueJob ( "PromiseJobs", PromiseResolveThenableJob, ??promise, resolution, thenAction?)

回答by zzzzBov

The quick-and-dirty solution is to test if the object has a thenfunction:

快速而肮脏的解决方案是测试对象是否具有then函数:

if (typeof message.then === 'function') {
    //assume it's a Deferred or fits the Deferred interface
} else {
    //do other stuff
}