2

私の Firefox 拡張機能は、Web サイトがアドオン機能にアクセスするために使用できる JavaScript 関数を提供しています。Web サイトはこの関数を呼び出し、2 つのコールバックを提供します。

ウェブサイト コード:

function onButtonClick() {
  var callbackSuccess = function() { alert("Yeah!"); };
  var callbackError = function() { alert("Oh no!"); };
  if (window.magicAddon) { // Check if my addon is installed
    magicAddon.doStuff(callbackSuccess, callbackError);
  }
}

コンテンツ スクリプト:

unsafeWindow.magicAddon = {
  doStuff: function(callbackSuccess, callbackError) {
    // Bind the two callbacks to events. The addon will fire one of them
    self.port.on("doStuffSuccess", callbackSuccess);
    self.port.on("doStuffError", callbackError);

    // Fire the event that lets the addon do stuff
    self.port.emit("doStuff");
  }
};

これは最初の呼び出しではうまく機能しますが、次に Web サイトが doStuff() を呼び出すと、新しいリスナーが追加され、alert() が 2 回実行されます。次回は 3 つのアラートなど。

リスナーが加算されることをエレガントに回避する方法はありますか? イベントタイプを完全にクリアできますか?

これまでに機能しなかったこと:

  • self.port.once(..)2 つのコールバック イベントがあるため、代わりに使用します。発生したイベントのみがクリアされ、もう 1 つのイベントは残り、次のイベントに加算されます。
  • self.port.removeListener古いコールバック参照がないため、新しいリスナーを登録する前に、古いものを で削除します。

問題は、イベントリスナーを削除する方法に似ているようです? 、彼は 1 つのコールバック リスナーを使用しているため、 を使用できるということだけself.port.once(..)です。

4

2 に答える 2

1

他のコールバックを使用self.port.onceしてから手動で削除できます。

doStuff: function(callbackSuccess, callbackError) {
  // Bind the two callbacks to events. The addon will fire one of them
  self.port.once("doStuffSuccess", function() {
    callbackSuccess();
    self.port.removeListener(callbackError);
  });
  self.port.once("doStuffError", function() {
    callbackError();
    self.port.removeListener(callbackSuccess);
  });

  // Fire the event that lets the addon do stuff
  self.port.emit("doStuff");
}

コンテンツ スクリプトを使用しているため、イベント タイプを完全にクリアすることはできません。メインのアドオン コードでのみ、低レベル API を使用してクリアできます。

ただし、unsafeWindowこの種の機能を提供することは避けることをお勧めします。安全ではないからです。API を非同期に維持する場合は、postMessageコンテンツ スクリプトとページの間のパイプラインを使用して、同じことを行うことができます。また、postMessages 呼び出しの抽象化を公開する Web サイトに含めることができる個別の JavaScript ファイルを提供します (例: magicAddon.doStuff())。必要に応じて、そのスクリプトをアドオンから Web サイトに自動的に挿入することもできます。

このメカニズムの処理は確かにもう少し複雑ですが、 の使用を避けることができますunsafeWindow

コンテンツ スクリプト通信の詳細については、こちらを参照してください。

それが役に立てば幸い!

更新doStuff:コメントに答えるには、通話アクティビティを追跡するための変数が必要です。

doStuff: function() {
  var executing = false;

  return function(callbackSuccess, callbackError) {
      if (executing)
        return;

      executing = true;

      // Bind the two callbacks to events. The addon will fire one of them
      self.port.once("doStuffSuccess", function() {
        executing = false;
        callbackSuccess();
        self.port.removeListener(callbackError);
      });
      self.port.once("doStuffError", function() {
        executing = false;

        callbackError();
        self.port.removeListener(callbackSuccess);
      });

      // Fire the event that lets the addon do stuff
      self.port.emit("doStuff");
  }
}()

()末尾に注意してください。基本的にこのように、最後に に設定されたdoStuff関数は、最初に割り当てた関数の結果です。doStuffこのようにして、変数が存在するメソッドのクロージャーを作成しexecuting、実行が既に行われているかどうかを追跡doStuffし、他のdoStuff呼び出しを破棄するために、実行が完了するまで追跡します。

注: この場合、javascript には必要ない場合でも、この関数が「自己実行」であることを識別するために、その関数を括弧で囲むのは良い慣例かもしれません: `doStuff: (function() {...}())

このジョブのオブジェクトのプロパティを使用することもできますmagicAddonが、その場合は公開されます。

于 2012-12-12T02:38:41.007 に答える
1

magicAddon.doStuff()最初の呼び出しで応答を受け取る前に、Web サイトが再度呼び出す可能性を考慮する必要があります。そのため、任意の時点で複数の呼び出しが実行される可能性があります。適切なリスナーを呼び出すようにしてください。また、いずれかのコールバックが発生した場合は、両方のリスナーを削除する必要があります。そうしないと、メモリ リークが発生します。これがどのように機能するかを次に示します。

doStuff: function(callbackSuccess, callbackError) {
  // Generate a random call ID
  var callID = Math.random();

  // Bind the two callbacks to events. Make sure to only act on events with the
  // right call ID.
  function onSuccess(id) {
    if (id == callID) {
      callbackSuccess();
      removeListeners();
    }
  }
  function onError(id) {
    if (id == callID) {
      callbackError();
      removeListeners();
    }
  }
  function removeListeners() {
    self.port.removeListener("doStuffSuccess", onSuccess);
    self.port.removeListener("doStuffError", onError);
  };
  self.port.on("doStuffSuccess", onSuccess);
  self.port.on("doStuffError", onError);

  // Fire the event that lets the addon do stuff
  self.port.emit("doStuff", callID);
}

これは、ランダムな呼び出し ID を使用して呼び出しを識別します。コード処理イベントはパラメーターとして呼び出し ID を取得し、正しいコールバックが呼び出されることを確認するために、またはイベントdoStuffでそれを送り返す必要があります。doStuffSuccessdoStuffError

于 2012-12-12T09:07:39.313 に答える