0

私は、Delicious と同様の方法でリンクを保存する Chrome 拡張機能を作成しています。拡張機能の一部として、保存しているリンクのキーワードを保存したいと考えています。このキーワードを取得するために、次の関数を使用します。

EXT.get_keywords = function (tab) {
var keywords = [];

    if(tab) {
        chrome.tabs.executeScript(tab.id, {file:"js/keywords.js"},
            (function(keywords) {
                return function (res) {
                    var kw_str = res && res[0];
                    if (kw_str) {
                        keywords.push.apply(keywords, kw_str.split(","));
                    }
                }
            })(keywords));
    }
    console.log(keywords);
    return keywords;
}    

「js/keywords.js」ファイルの内容は次のとおりです。

var metas = document.getElementsByTagName('meta'),
    i = 0,
    result = "";

for (i = 0; i < metas.length; i++) {
    if (metas[i].getAttribute("name") === "keywords") {
        result = metas[i].getAttribute("content");
        break;
    }
}
result;

「js/keywords.js」スクリプトは非常にうまく機能し、コールバック関数は、存在する場合はキーワード タグからコンテンツ文字列を取得しますが、コールバックが実行された後、キーワード変数は常に[]. 何か案が?

console.log(keywords)PS:行にブレークポイントを付けてスクリプトを実行すると機能しますが、ブレークポイントが存在しない場合は機能しないため、非常に奇妙です:S.

4

1 に答える 1

1

これはchrome.tabs.executeScript()の非同期性によるものです。関数からキーワードを返すことはできません。get_keywords実際には空の配列を返すことはできkeywordsますが、関数が終了するまで値は入力されません。
(ほとんどのchrome.* API は非同期であるため、拡張機能全体の「スタイル」をこれに適合させる必要があります。)

それで、何が起こるのですか?

実行順序は次のとおりです。

  1. 空のkeywords配列が初期化されます ( var keywords = [];)。
  2. executeScriptバックグラウンドでいくつかの非公式なものを開始すると呼ばれます。
  3. (function(keywords) {...})(keywords)が呼び出され、(まだ空のkeywords配列にバインドされている) コールバックを返し、登録します。コールバックは、インジェクションと実行が終了するたびに呼び出されます。
  4. executeScript戻ります (コールバックはまだ実行されていないことに注意してください - バックグラウンドで非同期的に処理を行っています。つまり、JS の注入と実行を行っています)。
  5. console.log(keywords);(まだ空の)keywords配列をログに記録します。
  6. まだ空のkeywords配列が返されます ( return keywords;)。
  7. 非同期の注入/実行が終了し、コールバックが呼び出されてkeywords配列に値が入力されます。

ブレークポイントを有効にすると、ステップ 5 に到達する前に注入/実行が完了する時間があるため、keywords配列は返される前に設定されます。


これを示すサンプル拡張機能:

background.jsでは、keywords配列は 2 回ログに記録されます。1 回は返される前getKeywords()、もう 1 回はコールバックに入力された後です。ご覧のとおり、ログイン コールバックは、空の配列が返された後に実行されます。 getKeywords()

content.js:

var metas = document.getElementsByTagName("meta");
var result = "";
for (var i = 0; i < metas.length; i++) {
    var meta = metas[i];
    if (meta.name && (meta.name.toLowerCase() === "keywords")) {
        result = meta.content;
        break;
    }
}
result;

background.js:

function getKeywords(tab) {
    var keywords = [];

    chrome.tabs.executeScript(
            tab.id,
            { file: "/fg/content.js" },
            (function(keywords) {
                return function(resultArr) {
                    if (!chrome.runtime.lastError && resultArr[0]) {
                        keywords.push.apply(keywords, resultArr[0].split(","));
                    }
                    console.log("After: ", keywords);
                }
            })(keywords));
    console.log("Before: ", keywords);
}

chrome.browserAction.onClicked.addListener(getKeywords);

マニフェスト.json:

{
    "manifest_version": 2,
    "name":    "Test Extension",
    "version": "0.0",

    "background": {
        "persistent": false,
        "scripts": ["./bg/background.js"]
    },

    "browser_action": {
        "default_title": "Test Extension"
    },

    "permissions": ["activeTab"]
}
于 2013-11-11T12:28:13.633 に答える