sendMessageにcallbackを指定せずにonMessageのリスナーで非同期レスポンスをするとエラーが発生する

4,016 views
Skip to first unread message

yamaji

unread,
Sep 19, 2012, 5:34:25 AM9/19/12
to chrome-api-d...@googlegroups.com
yamajiと申します。

content scriptsとbackground pagesでメッセージをやり取りする場面で
"Attempting to use a disconnected port object"というエラーが出て悩んでいました。

いろいろ試しているうちに
chrome.extension.sendMessageにcallback引数を指定せずに
chrome.extension.onMessageのリスナーで非同期なレスポンスをすると
上記のエラーが発生するのが分かりました。

具体的には下のようなコードでエラーを再現できます。

[content scripts]
chrome.extension.sendMessage('request message');

[background pages]
chrome.extension.onMessage.addListener(function(req, sender, sendResponse){
setTimeout(function(){
sendResponse('response message');
}, 1000);
return true;
});


・非同期でないレスポンスの場合はsendMessage側にコールバックを指定しなくてもOK
・非同期なレスポンスをする場合はsendMessage側にコールバックを指定しないとエラー
ということになると思うのですが、これはこういう仕様という理解でよろしいでしょうか。

なども見てみましたが詳しくは書かれていないように思いました。

また、非同期なレスポンスだとエラーとなる内部的な仕組みについて
分かる方がいらっしゃれば参考までに教えて頂ければと思います。

一応、再現用のextensionをあげておきます。

Yasushi Ando

unread,
Sep 21, 2012, 10:01:15 AM9/21/12
to chrome-api-d...@googlegroups.com
こんにちは。あんどうと申します。

そもそもchrome.extension.onMessageリスナーのsendResponseはchrome.extension.sendMessage呼び出し時に指定されるコールバックに結果を返すためのものです。そのため(想像ですが)sendMessageにコールバックが指定されていない場合はonMessageリスナーの戻り値の真偽にかかわらずportを切断するような実装になっているのではないでしょうか。つまり(くれぐれも想像ですが)sendMessageにコールバックがない場合は常にonMessageリスナーの戻り値をreturn
false; としたのと同じ動作をしているんだと思います。


2012/9/19 yamaji <yamayam...@gmail.com>:

> --
> このメールは Google グループのグループ「Chrome API Developers JP」の登録者に送られています。
> このグループに投稿するには、chrome-api-d...@googlegroups.com にメールを送信してください。
> このグループから退会するには、chrome-api-develop...@googlegroups.com
> にメールを送信してください。
> 詳細については、http://groups.google.com/group/chrome-api-developers-jp?hl=ja
> からこのグループにアクセスしてください。

--
ANDO Yasushi
- and...@gmail.com
- http://d.hatena.ne.jp/technohippy/
- http://twitter.com/technohippy

yamaji

unread,
Sep 23, 2012, 10:56:56 PM9/23/12
to chrome-api-d...@googlegroups.com
yamajiです。
あんどうさん、レスありがとうございます^^
想像でも見解を聞かせてもらえるのは助かります。

同じsendMessageにコールバックがないという条件でも、
onMessageリスナーのレスポンスが非同期/非同期でないによって
エラーが発生する/しないという違いが出るところに
何となく引っかかってまして・・

こういう風に考えればすっきるするんじゃない、というような
アドバイスはないでしょうか^^;


2012年9月21日金曜日 23時01分36秒 UTC+9 Yasushi Ando:
こんにちは。あんどうと申します。

そもそもchrome.extension.onMessageリスナーのsendResponseはchrome.extension.sendMessage呼び出し時に指定されるコールバックに結果を返すためのものです。そのため(想像ですが)sendMessageにコールバックが指定されていない場合はonMessageリスナーの戻り値の真偽にかかわらずportを切断するような実装になっているのではないでしょうか。つまり(くれぐれも想像ですが)sendMessageにコールバックがない場合は常にonMessageリスナーの戻り値をreturn
false; としたのと同じ動作をしているんだと思います。

-- 

Yasushi Ando

unread,
Sep 24, 2012, 1:41:41 AM9/24/12
to chrome-api-d...@googlegroups.com
yamajiさん、こんにちは。

なんというか想像で答えているだけなので大変心苦しいんですが、基本的にはonMessageリスナーでreturn
trueするとsendResponseの処理が終了するまでportは有効ですが、return
falseするとonMessageリスナーが終了したときにportは切断されます。

なので

[content scripts]
chrome.extension.sendMessage('request message', function(res)
{console.log(res)});

上記のようにsendMessageにコールバックを渡していても

[background pages]
chrome.extension.onMessage.addListener(function(req, sender, sendResponse){
setTimeout(function(){
sendResponse('response message');
}, 1000);

return false;
});

上記のようにreturn falseとsetTimeoutを組み合わせるとエラーになります。

[background pages]
chrome.extension.onMessage.addListener(function(req, sender, sendResponse){

sendResponse('response message');
return false;
});

もちろんreturn falseしていてもsendResponseをその場で実行すればエラーになりません。
(ここまではドキュメントに書かれているので事実です)

これはyamajiさんが当初書かれていた動作と同じように見えるので
「sendMessageにコールバックがない場合は常にonMessageリスナーの戻り値をreturn false; としたのと同じ動作をする」
と解釈するとすっきりするかなと思ったんですがダメでしょうか?
(ただし、これは想像です)

上記のような動作になるのは
「そもそもonMessageリスナーのsendResponseはsendMessage呼び出し時に指定されるコールバックに結果を返すためのものだから」
と考えるとわりと妥当だと思いますし。

2012/9/24 yamaji <yamayam...@gmail.com>:

> --
> このメールは Google グループのグループ「Chrome API Developers JP」の登録者に送られています。
> このグループに投稿するには、chrome-api-d...@googlegroups.com にメールを送信してください。
> このグループから退会するには、chrome-api-develop...@googlegroups.com
> にメールを送信してください。
> 詳細については、http://groups.google.com/group/chrome-api-developers-jp?hl=ja
> からこのグループにアクセスしてください。

--

yamaji

unread,
Sep 24, 2012, 8:05:47 AM9/24/12
to chrome-api-d...@googlegroups.com
あんどうさん、ありがとうございます。

なるほど~、return false; としたのと同じ動作 というのは
そういうことでしたか。

onMessageリスナーの処理後にコールバックの有無をみて
自動的にportを切断する処理が走る、と考えるとすっきりします。

そもそもonMessageリスナーのsendResponseはsendMessage呼び出し時に指定されるコールバックに結果を返すためのものだから」
というのもごもっともです。

ちなみに私が、リクエスト側でコールバックを指定していないのに
レスポンス側でsendResponseするようにしてしまっていたのは
  • レスポンス側は、値を返す必要がない場合でもとりあえず処理結果だけは常にレスポンスする
  • リクエスト側で処理結果すら見る必要がない場合はコールバックを指定しない
という作りにしていたからでした。
コールバックの有無とレスポンスの有無はきちんと合わせるように
しようと思います。

時間が取れたら、実際どういうコードになっているのか読んでみようと思います。



2012年9月24日月曜日 14時42分03秒 UTC+9 Yasushi Ando:

> このグループに投稿するには、chrome-api-develo...@googlegroups.com にメールを送信してください。
> このグループから退会するには、chrome-api-developers-jp+unsubscribe@googlegroups.com


> にメールを送信してください。
> 詳細については、http://groups.google.com/group/chrome-api-developers-jp?hl=ja
> からこのグループにアクセスしてください。

Yasushi Ando

unread,
Sep 24, 2012, 9:51:40 AM9/24/12
to chrome-api-d...@googlegroups.com
> レスポンス側は、値を返す必要がない場合でもとりあえず処理結果だけは常にレスポンスする
> リクエスト側で処理結果すら見る必要がない場合はコールバックを指定しない

なるほど、モチベーションがよく分からなかったんですがそういうことだったんですか。
そうすると行儀は悪いですが例外を取って捨てるくらいしか私には思いつかないですねぇ・・・。

chrome.extension.onMessage.addListener(function(req, sender, sendResponse){
 setTimeout(function(){
  try {
   sendResponse('response message');
  } catch(e) { /* ignore */ }
 } , 1);
 return true;
});

> 時間が取れたら、実際どういうコードになっているのか読んでみようと思います。

謎が解けたらぜひ教えて下さい。私も想像で答えるだけでは申し訳ないのでざっと読んではみたんですが力尽きましたw


2012/9/24 yamaji <yamayam...@gmail.com>:

>> > このグループに投稿するには、chrome-api-d...@googlegroups.com にメールを送信してください。
>> > このグループから退会するには、chrome-api-develop...@googlegroups.com


>> > にメールを送信してください。
>> > 詳細については、http://groups.google.com/group/chrome-api-developers-jp?hl=ja
>> > からこのグループにアクセスしてください。
>>
>> --
>> ANDO Yasushi
>> - and...@gmail.com
>> - http://d.hatena.ne.jp/technohippy/
>> - http://twitter.com/technohippy
>

> --
> このメールは Google グループのグループ「Chrome API Developers JP」の登録者に送られています。

> このグループに投稿するには、chrome-api-d...@googlegroups.com にメールを送信してください。
> このグループから退会するには、chrome-api-develop...@googlegroups.com

yamaji

unread,
Sep 25, 2012, 9:51:58 PM9/25/12
to chrome-api-d...@googlegroups.com
そうですね、catchして無視するというやり方がありますね。
とりあえず今回はコールバックとレスポンスの有無を合わせるようにしておきます^^;

それから
miscellaneous_bindings と event_bindings のjsコードを中心に
dev toolでデバッグしながら見てみました。
気付いたことを書いておきますね。

 1. sendMessageでコールバックが指定されていない場合は、onMessageリスナーが実行される時点で既にportが切断されている
    (実際に切れているかは分からないですが、ネイティブコード側のHasPortDataでfalseが返る状態になっている)
 2. ブレークポイントを設定して実行すると、非同期でないsendResponseをした場合も "Attempting to use a disconnected port object"エラーが発生する
    (エラーがキャッチされてコンソールにエラーが表示される)

はじめ1に気付いた時に
それなら非同期でないsendResponseでもエラーになるんじゃないの、と思って
調べてみると2の現象が確認できました。

ただし、ブレークポイントを設定する場所によってエラーが捕捉されたりされなかったりします。
例えばevent_bindingsの373行目にBPを設定するとエラーが出てcatchの方へ流れますが、
374行目にBPを設定してもエラーが出ません。

371:    for (var i = 0; i < listeners.length; i++) {
372:      try {
373:        var result = this.dispatchToListener(listeners[i].callback, args);
374:        if (result !== undefined)
375:          results.push(result);
376:      } catch (e) {
377:        console.error("Error in event handler for '" + this.eventName_ +
378:                      "': " + e.message + ' ' + e.stack);
379:      }
380:    }
381:    if (results.length)
382:      return {results: results};

というところまで分かりました。

sendMessageでコールバックを指定しないでsendResponseすると
非同期かどうかに関わらず同じエラーになるということであれば
なおすっきりするのですが。


2012年9月24日月曜日 22時52分02秒 UTC+9 Yasushi Ando:

> --
> このメールは Google グループのグループ「Chrome API Developers JP」の登録者に送られています。

> このグループに投稿するには、chrome-api-develo...@googlegroups.com にメールを送信してください。
> このグループから退会するには、chrome-api-developers-jp+unsubscribe@googlegroups.com


> にメールを送信してください。
> 詳細については、http://groups.google.com/group/chrome-api-developers-jp?hl=ja
> からこのグループにアクセスしてください。

Yasushi Ando

unread,
Sep 25, 2012, 11:40:42 PM9/25/12
to chrome-api-d...@googlegroups.com
> 1. sendMessageでコールバックが指定されていない場合は、onMessageリスナーが実行される時点で既にportが切断されている
> (実際に切れているかは分からないですが、ネイティブコード側のHasPortDataでfalseが返る状態になっている)

だとすると、port切断は別スレッドに任されてて非同期なので、タイミング次第ではたまたまsendResponseのときにまだportが生きてることがあるだけで、基本的にはsendMessageでコールバックを指定してないときはonMessageのリスナーでsendResponseは呼べないというのが正しい動きっぽいですね。

chromeの実装分かってる人だれか出てきて教えてくれないですかねー。

2012/9/26 yamaji <yamayam...@gmail.com>:

>> > このグループに投稿するには、chrome-api-d...@googlegroups.com にメールを送信してください。
>> > このグループから退会するには、chrome-api-develop...@googlegroups.com


>> > にメールを送信してください。
>> > 詳細については、http://groups.google.com/group/chrome-api-developers-jp?hl=ja
>> > からこのグループにアクセスしてください。
>>
>> --
>> ANDO Yasushi
>> - and...@gmail.com
>> - http://d.hatena.ne.jp/technohippy/
>> - http://twitter.com/technohippy
>

> --
> このメールは Google グループのグループ「Chrome API Developers JP」の登録者に送られています。

> このグループに投稿するには、chrome-api-d...@googlegroups.com にメールを送信してください。
> このグループから退会するには、chrome-api-develop...@googlegroups.com

yamaji

unread,
Oct 2, 2012, 4:52:15 AM10/2/12
to chrome-api-d...@googlegroups.com
以降、特に理解が深まることもなく・・・そのままです^^;

ブレイクポイント入れて止めながら実行するとport切断済のエラーが出るというのも、あんどうさんの仰るようにport切断が非同期ということで納得がいきます。
ということで、
エラーは出なくても、コールバックが指定されてない時にsendResponseを呼ぶのはNGとしてコーディングするようにします。



2012年9月26日水曜日 12時41分04秒 UTC+9 Yasushi Ando:
>  1. sendMessageでコールバックが指定されていない場合は、onMessageリスナーが実行される時点で既にportが切断されている
>     (実際に切れているかは分からないですが、ネイティブコード側のHasPortDataでfalseが返る状態になっている)

だとすると、port切断は別スレッドに任されてて非同期なので、タイミング次第ではたまたまsendResponseのときにまだportが生きてることがあるだけで、基本的にはsendMessageでコールバックを指定してないときはonMessageのリスナーでsendResponseは呼べないというのが正しい動きっぽいですね。

chromeの実装分かってる人だれか出てきて教えてくれないですかねー。

-- 

Reply all
Reply to author
Forward
0 new messages