$ .Deferred.resolve是"不是函数"在Firefox事件监听器中

时间:2017-06-16 15:17:33

标签: javascript jquery firefox jquery-deferred dom-events

当我在Firefox下的沙盒Greasemonkey脚本中将jQuery延迟对象传递给窗口事件处理程序时,由于某种原因,resolve函数未定义。

考虑以下测试用户脚本,它必须在已加载jQuery的页面上运行(这是我可以获得的最小值):

// ==UserScript==
// @name        Deferred Event Test
// @namespace   jasonc
// @description Deferred Event Test
// @include     /https?:\/\/*/
// @version     1
// @grant       GM_getValue
// @run-at      document-idle
// ==/UserScript==

// Event listener calls ev.detail.deferred.resolve().
window.addEventListener('test-event-1234', function (ev) {
  try {
    console.log(ev.detail.deferred);
    console.log(ev.detail.deferred.resolve);
    ev.detail.deferred.resolve(42);
  } catch (e) {
    console.error(e);
  }
});

function runTest ($) {

  // Test resolve directly.
  $.Deferred(function (def) {
    def.resolve(99);
  }).then(function (v) {
    console.log(`resolved: ${v}`);
  });

  // Test through event listener.
  $.Deferred(function (def) {
    window.dispatchEvent(new CustomEvent('test-event-1234', { detail: {
      deferred: def
    }}));
  }).then(function (v) {
    console.log(`resolved: ${v}`);
  });

}

// Inject script into page and run it.
let script = document.createElement('script');
script.type = 'text/javascript';
script.textContent = `(${runTest.toString()})(window.jQuery)`;
document.body.appendChild(script);

值得注意的是:

  • document-idle运行,因此jQuery已经可用。
  • 拥有非none授权,以便进行沙盒处理。
  • runTest功能注入页面并从那里运行。

假设要做的是发送" test-event-1234"将ev.detail.deferred设置为jQuery延迟对象,然后等待事件侦听器解析它。但是,它会在控制台中输出以下内容:

resolved: 99

Object { resolve: Deferred/</e[f[0]](), resolveWith: fireWith(), 
         reject: Deferred/</e[f[0]](), rejectWith: fireWith(), 
         notify: Deferred/</e[f[0]](), notifyWith: fireWith(), 
         state: state(), always: always(), then: then(),
         promise: promise(), 4 more… } 

undefined

TypeError: ev.detail.deferred.resolve is not a function
Stack trace:
    // indicates the resolve() call in the event listener

&#34;已解决:99&#34;是直接(无事件监听器)测试的输出,表明此时确实存在resolve()

下一行输出是事件处理程序中的console.log(ev.detail.deferred),它显示resolve在某个时刻存在(并且至少确认我将正确的对象传递给事件处理程序),但是console.log(ev.detailed.deferred.resolve)(我在Kevin B's suggestion之后添加,以免被异步控制台输出问题误导)显示undefined

然后,呼叫失败,说明ev.detail.deferred.resolve不是函数。

以下事项使这项工作正常:

  • 在Chrome上运行它(Tampermonkey),没问题。
  • 将授权更改为@grant none,以便它不会被沙箱化。

这里发生了什么?我已经盯着这一段时间,我非常有信心没有任何错别字。我无法弄清楚为什么resolve未定义,特别是考虑到&#34;直接&#34;在调用resolve之前进行测试,没有任何问题。有没有办法使这项工作?

附加测试

在(删除)评论中提出了一些测试。到目前为止,所有这些都产生与上述相同的结果:

创建延迟对象之前的延迟:

  // Test with delay #1
  window.setTimeout(function () {
    $.Deferred(function (def) {
      window.dispatchEvent(new CustomEvent('test-event-1234', { detail: {
       deferred: def
      }}));
    }).then(function (v) {
      console.log(`resolved: ${v}`);
    });
  }, 1000);

调度事件前的延迟:

  // Test with delay #2
  $.Deferred(function (def) {
    window.setTimeout(function () {
      window.dispatchEvent(new CustomEvent('test-event-1234', { detail: {
        deferred: def
      }}));
    }, 2000);
  }).then(function (v) {
    console.log(`resolved: ${v}`);
  });

在致电resolve之前延迟事件:

// Event listener calls ev.detail.deferred.resolve() after a delay.
window.addEventListener('test-event-delayed-1234', function (ev) {
  window.setTimeout(function () {
    try {
      console.log(ev.detail.deferred.resolve);
      ev.detail.deferred.resolve(42);
    } catch (e) {
      console.error(e);
    }
  }, ev.detail.timeout);
});

... then, elswhere: ...

  // Test with delay #3
  $.Deferred(function (def) {
    window.dispatchEvent(new CustomEvent('test-event-delayed-1234', { detail: {
      deferred: def,
      timeout: 3000
    }}));
  }).then(function (v) {
    console.log(`resolved: ${v}`);
  });

0 个答案:

没有答案