🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

同期

複数のディレクトリに存在するファイルを更新した場合に、すべてのファイルにも更新が行われる事、又は、同じ記憶領域に同時にアクセスして内容の整合性が失われてしまう事をを防ぐ制御などを同期と呼びます。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Chrome extension

Chrome拡張機能

Q&A

解決済

1回答

9847閲覧

chrome拡張機能のchrome.runtime.sendMessageの同期処理が正しく挙動しない

Daimian

総合スコア53

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

同期

複数のディレクトリに存在するファイルを更新した場合に、すべてのファイルにも更新が行われる事、又は、同じ記憶領域に同時にアクセスして内容の整合性が失われてしまう事をを防ぐ制御などを同期と呼びます。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Chrome extension

Chrome拡張機能

1グッド

1クリップ

投稿2019/09/26 03:10

編集2019/09/26 05:32

前提・実現したいこと

下記の「該当のソースコード」内にあるcontent_script.jsからchrome.runtime.sendMessageを実行。background.jsのchrome.runtime.onMessage.addListenerからの返り値によって以降の処理を分岐するスクリプトを書いています。

(蛇足ですが、人の顔を配置してクリックしたら笑う、笑っているときはisSmilingの値をtrueに、笑ってないときはisSmilingの値をfalseに設定して、クリックするごとに笑う→普通の顔→笑う→普通の顔を繰り返すことが期待している挙動です)

発生している問題・エラーメッセージ

content_scriptから吐き出されるconsole.logを確認してみると、

script.js:70 result after chrome.runtime.sendMessage undefined script.js:48 response in move undefined script.js:70 result after chrome.runtime.sendMessage undefined script.js:66 response in chrome.runtime.sendMessage false script.js:66 response in chrome.runtime.sendMessage true

という順番で吐き出されてしまい、chrome.runtime.sendMessageの実行結果を待たずに次の処理に行ってしまっています。

該当のソースコード

content_script.js

javascript

1// chromeの拡張機能用APIを使用しないと取得できない 2const nomalFaceImgUrl = chrome.runtime.getURL('images/human_icon_48.png'); 3const smileFaceImgUrl = chrome.runtime.getURL('images/human_icon_48_smile.png'); 4 5// APIへのリクエスト準備 6let smileActionGetPayLoad = { 7 method: 'get', 8 query: 'smileAction', 9 params: null, 10}; 11let smileActionPostPayLoad = { 12 method: 'patch', 13 query: 'smileAction', 14 params: true, 15}; 16 17/** 18 * 画像を配置する 19 * @param {integer} wx - 右からの位置 20 * @param {integer} wy - 下からの位置 21 */ 22function deploy(wx, wy) { 23 // 配置準備 24 const human = document.createElement('img'); 25 human.src = nomalFaceImgUrl; 26 27 // スタイル定義 28 human.id = 'human'; 29 human.style.position = 'fixed'; 30 human.style.right = wx; 31 human.style.bottom = wy; 32 human.style.zIndex = 9999; // 一番上に表示するために適当な値 33 34 // onclickした時に実行するもの 35 human.onclick = move; 36 37 // 配置する 38 const objBody = document.getElementsByTagName('body').item(0); 39 objBody.appendChild(human) 40}; 41 42/** 43 * 画像を変化させる 44 */ 45function move() { 46 const response = coreAPI(smileActionGetPayLoad); 47 console.log('response in move', response); 48 49 if (response == true) { 50 document.getElementById('human').src = nomalFaceImgUrl; 51 smileActionPostPayLoad.params = false; 52 coreAPI(smileActionPostPayLoad); 53 } 54 55 document.getElementById('human').src = smileFaceImgUrl; 56 coreAPI(smileActionPostPayLoad); 57}; 58 59/** 60 * 画像を配置する 61 * @param {Object} payLoad - リクエストするための引数 62 */ 63function coreAPI(payLoad) { 64 const result = chrome.runtime.sendMessage(payLoad, function(response) { 65 console.log('response in chrome.runtime.sendMessage', response); 66 return response 67 }); 68 69 console.log('result after chrome.runtime.sendMessage', result); 70 71 return result; 72}; 73 74 75// 実行 76deploy(0, 0); 77

background.js

javascript

1// 変数をまとめておく 2let store = { 3 isSmiling: false, 4} 5 6/** 7 * フロントに当たるcontent_scripts.jsからのリクエストを処理する 8 * @param {Object} msg - フロントでpayLoad={{method: String}, {query: String}, {params: String}}のような形で設定した引数が入る 9 * @param {integer} sender - 送り元の情報。この拡張機能のIDや、リクエスト元のURL、tabの情報などが取れる 10 * @param {function} sendResponse - これを実行して引数にフロントに返したい内容を入れる。 11 */ 12chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { 13 if (msg.query == 'smileAction') { 14 smileAction(msg); 15 }; 16 sendResponse(store.isSmiling); 17 18 return true; 19}); 20 21/** 22 * 笑顔アクションに関する挙動 23 * @param {Object} msg - フロントでpayLoad={{method: String}, {query: String}, {params: String}}のような形で設定した引数が入る 24 */ 25function smileAction(msg) { 26 if (msg.method == 'get') { 27 return store.isSmiling; 28 } else if (msg.method == 'patch') { 29 store.isSmiling = msg.params 30 return store.isSmiling 31 }; 32}; 33

試したこと

https://qiita.com/Tachibana446/items/ab15021099d54d1209c2
この記事で指摘されている内容(chrome.runtime.onMessage.addListenerの中身にreturn trueを入れる)を試しましたが、結果、上記の状態のままです。

補足情報(FW/ツールのバージョンなど)

不要かもしれませんが、manifest.jsonの内容も記します。

json

1{ 2 "manifest_version": 2, 3 "name": "test", 4 "version": "0.1", 5 "content_scripts": [ { 6 "matches": [ "http://*/*", "https://*/*" ], 7 "js": ["script.js"] 8 } ], 9 "background": { 10 "scripts": ["background.js"] 11 }, 12 "page_action": { 13 "default_icon": { 14 "16": "images/grandma_icon_16.png", 15 "48": "images/grandma_icon_48.png", 16 "128": "images/grandma_icon_128.png" 17 } 18 }, 19 "icons": { 20 "16": "images/grandma_icon_16.png", 21 "48": "images/grandma_icon_48.png", 22 "128": "images/grandma_icon_128.png" 23 }, 24 "web_accessible_resources": [ 25 "images/*" 26 ] 27}

何卒、よろしくお願いいたします。

Poomi👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

js

1 const result = chrome.runtime.sendMessage(payLoad, function(response) { 2 console.log('response in chrome.runtime.sendMessage', response); 3 return response 4 });

とありますが、chrome.runtime.sendMessage()は非同期な関数であり、戻り値はありません。return response することで result にその値を受け取ろうとしていませんか?それはできません。このあたりは「js 非同期処理 コールバック」などで検索して調べてみてください。

コールバックの形で書くとしたら次のようになります。

js

1function coreAPI(payLoad, callback) { 2 chrome.runtime.sendMessage(payLoad, callback); 3}; 4 5function move() { 6 coreAPI(smileActionGetPayLoad, function (response) { 7 // ここに response を使った続きの処理 8 }); 9};

Promise を使うと次のようになります。

js

1function coreAPI(payLoad) { 2 return new Promise(function (resolve) { 3 chrome.runtime.sendMessage(payLoad, function(response) { 4 resolve(response) 5 }); 6 }); 7}; 8 9function move() { 10 coreAPI(smileActionGetPayLoad).then(function (response) { 11 // ここに response を使った続きの処理 12 }); 13};

さらに async/await を使うと次のようになります。

js

1function coreAPI(payLoad) { 2 return new Promise(function (resolve) { 3 chrome.runtime.sendMessage(payLoad, function(response) { 4 resolve(response) 5 }); 6 }); 7}; 8 9async function move() { 10 const response = await coreAPI(smileActionGetPayLoad) 11 // ここに response を使った続きの処理 12};

投稿2019/09/26 08:29

karamarimo

総合スコア2555

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Daimian

2019/09/26 10:36

非常に勉強になりました。ご指摘いただきました内容で試したところ、期待通りの挙動になりました。 「非同期な関数で戻り値はありません」の部分の理解が自分に不足しているため、今から勉強をします。 素晴らしいご回答、誠にありがとうございます!
karamarimo

2019/09/26 10:49

補足しておくと、非同期な関数ならば戻り値がない、というわけではないです。 ただ const result = ...sendMessage(...) という形で今欲しい response を受け取ることはできない、ということです。
Daimian

2019/09/26 14:42

補足のコメントありがとうございます!この辺り理解が怪しいので学習の題材として非常に良い例だと思います。 return new Promise( )で呼び出し元であるcoreAPI( )にPromise関数を返し、async/awaitによりPromise関数のpendingが完了になるまでまち、完了したらその結果であるresponseを取得できる という流れだと理解しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問