質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

90.12%

(iframeで出来たsidebarから通信を行うため)background-fetchを実装したが上手く動かないパターンが有る。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 553

m0a

score 669

現在chrome-extensionを作っておりましてサイドバーを使う機能になっています。

サイドバー内のUI(iframeで出来ています)から制限なく通信を行う必要がありました。そこでbackground(event page)経由でfetchを行う
background-fetch(proxy-fetch)を実装し運用しておりました。
ところが特定の通信でエラーが発生していることがわかり、書き換えを試みたのですが動作が悪化してしまいました。
お手数ですが添削をお願いできませんでしょうか?

もしくはnpm packageで同じ目的のfetch代替のライブラリがありましたら教えていただけないでしょうか?

以下が既存の実装となります。

  • 変更前
// frontend側のfetchを代行する TODO 要リファクタリング
export interface FetchInterface {
  type: "fetch";
  input: RequestInfo;
  init?: RequestInit;
}

export interface ResultInterface {
  type: "result" | "error";
  result: Object;
}

// EventPageで本関数実行することでProxy側の準備を行う
export function backgroundFetchReady() {
  // eventページ以外で実行したら例外を発生
  expectBackground();
  chrome.runtime.onMessage.addListener((inFetchData, sender, sendResponse) => {
    let fetchData: FetchInterface = <FetchInterface>inFetchData;
    if (fetchData.type === undefined) {
      return;
    }
    if (fetchData.type !== "fetch") {
      return;
    }

    fetch(fetchData.input, fetchData.init).then(async result => {
      let json = {};
      try {
        json = await result.json();
      } catch (e) {
        // do nothing
      }
      sendResponse(<ResultInterface>{
        type: "result",
        result: {
          status: result.status,
          statusText: result.statusText,
          json: json
        }
      });
    });
    return true;
  });
}

// fetch代替関数 fetchに置き換えることで通信を行う
export async function backgroundFetch(input: RequestInfo, init?: RequestInit) {
  return new Promise<Response>((resolve, reject) => {
    chrome.runtime.sendMessage(
      <FetchInterface>{
        type: "fetch",
        input,
        init
      },
      (response: ResultInterface) => {
        if (response === undefined || response.type === undefined) {
          return;
        }
        // console.log('response', response);
        if (response.type === "result") {
          // console.log('in response.type is result', response.result);
          const res = <{
            status: number;
            statusText: string;
            json: Object;
          }>response.result;
          resolve(<Response>{
            status: res.status,
            statusText: res.statusText,
            json: () => res.json
          });
        } else {
          // console.log('in response.type is error', response.result);
          reject(response.result);
        }
      }
    );
  });
}

上記が暫く使っていたコードです。

fetch(fetchData.input, fetchData.init).then(async result  
の箇所でresultに値が入らないパターンがあったため想定外のエラーが出ていました。
それと asyncを使う箇所が気持ち悪かったのでbackgroundFetchReadyのみ以下のように書き換えました。

  • 変更後
export function backgroundFetchReady() {
  expectBackground();
  chrome.runtime.onMessage.addListener(
    async (inFetchData, sender, sendResponse) => {
      let fetchData: FetchInterface = <FetchInterface>inFetchData;
      if (fetchData.type !== "fetch") {
        return;
      }

      const response = await fetch(fetchData.input, fetchData.init).catch(
        e => logger.error(e)
      );
      if (response) {
        const json = await response.json();
        console.log(json) // debug用

        sendResponse(<ResultInterface>{
          type: "result",
          result: {
            status: response.status,
            statusText: response.statusText,
            json: json
          }
        });
      }
      return true;
    }
  );
}

こちらだとconsole.log(json) でレスポンスが返ってるのが見えるのに
backgroundFetchのResponseが戻らなくなるパターンが生じました

まず、代替ライブラリがパッケージなり標準であるようならそちらを使いたいので教えてください。
無けれは上記のコードの添削をおねがいしたいのです。

勝手なお願いで申し訳ございません。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

変更後のコードですが、chrome.runtime.onMessage.addListener()
で非同期でonMessageのresponseを返すためにreturn trueをしていますが、await fetch()でfetchのresponseがかえるまで待機した後return trueを返す形となり、backgroundFetchのResponseが戻らないのでは?と考えられます。
trueを返すのはあくまでも同期でないといけないと思いますので、fetchを行う部分を別関数にしてみてはいかがでしょう。

chrome.runtime.onMessage.addListener((inFetchData, sender, sendResponse) => {
// ...
    execFetch(fetchData.input, fetchData.init, sendResponse);
    return true;
});

async function execFetch(input, init, sendResponse) {
    const response = await fetch(fetchData.input, fetchData.init).catch(
        e => logger.error(e)
    );
    if (response) {
        const json = await response.json();
        console.log(json) // debug用

        sendResponse(<ResultInterface>{
            type: "result",
            result: {
                status: response.status,
                statusText: response.statusText,
                json: json
            }
        });
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/06 20:10

    ありがとうございます。完全に return trueの意図を失念していました。

    キャンセル

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

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる