13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

bookmarkletでfetchとasyncとawait

Last updated at Posted at 2019-07-06

#はじめに
過去にブックマークレットでjQuery使ってajaxをする記事を書きましたが、今時はjQueryを引っ張ってくることもなく標準装備のfetchをするのが主流っぽいので、ブックマークレットからfetchするサンプルです。

#今回のブックマークレット
みんな大好き楽天市場の検索結果から、リンクを探すブックマークレットです。rakuten.co.jpを開いたブラウザで実行してください。

##普通のコード

普通に書くとこんな感じになります。

javascript:(
function(){
  let URL = "https://search.rakuten.co.jp/search/mall/%E6%9C%AC/";
  fetch(URL)
    .then((response)=>response.text())
    .then((html)=>{

      //HTMLからリンク探す部分
      let regexp = /<a target="_top" href="([^"]*)" data-track-action="img">/g;
      let match;
      let matches = [];
      while ((match = regexp.exec(html))!== null) {
         matches.push(match[1]);
         console.log(match);
      }

      //リンクを使って楽しむ部分
      console.log(matches);
      
    });
}
)();

無事、fetchできました。
jQueryのajax関数と同じでfetchは非同期な関数なのでコードがネストします。実際にやりたいことを書く部分がインデントの奥に閉じ込められますね。

##fetchの非・非同期化
fetchが非同期なら、非同期をやめてしまいましょう。ブックマークレットですし、ちょっとくらい待たせしたところで、困るのは自分だけですからね。非同期を強制的に辞めることは何と呼べばいいのでしょう?同期化というのも変なので、非・非同期化と呼んでおきます。


javascript:(
async function(){
  let URL = "https://search.rakuten.co.jp/search/mall/%E6%9C%AC/";
  let links = await getLinks(URL);
  
  //リンクを楽しむ部分
  console.log(links);
  

  //HTMLを取りに行ってリンクを探す部分
  async function getLinks(url){
    let response = await fetch(url);
    let html = await response.text();
    let regexp = /<a target="_top" href="([^"]*)" data-track-action="img">/g;
    let match;
    let matches = [];
    while ((match = regexp.exec(html))!== null) {
        matches.push(match[1]);
    }
    return matches;
  }
}
)();

変更点は、

  • Linkを取り出すところをgetLinksに関数化し
  • 関数内のfetchの呼び出しにawaitを付け
  • その下のresponse.text()にもawaitを付け
  • getLinks関数をasyncで宣言をする。
  • getLinksの呼び出しにもawaitを付け、
  • 無名関数をasyncにする

かなり読みやすくになりました。関数を実行したらリンクが返ってくるという構造は、理解しやすいですね。

:warning: 注意点は、

awaitした後に取れたresponseを操作するからresponse.text()は普通に呼び出せそうな気がしますが、awaitしないと失敗します。
getLinsk自体がasync functionなので、呼び出すときにawait getLinks()にしないと結果を待たずに処理が進んでしまいます。
getLinks()の呼び出しをawaitにした関係で、先頭にある無名関数もasyncにしないといけません。

最後のやつはハマりました。

まとめると、こういうことです

  • asyncな関数(fetch, getLinks)は、awaitすると待てる。awaitしないと止まらずに進んでしまう
  • awaitを1つでも書いた自作の関数(getLinks, 先頭の無名関数)は、asyncを付けないと構文エラーになる

##Closure Compierのオプション設定
上のコードをClosure Compilerでコードをコンパイルすると、短くなるどころか長くなります。async, await, for-of など新しい文法ったスクリプトを、古いブラウザでも動くようにコンパイラが変換してくれます。余計なお世話ですね。
コードが長くなってもちゃんと動きますが、ブックマークレットのコードは短いほうがいいです。回避するためにClosure Compiler の設定を足す必要があります。

###初期設定でCompileする
image.png

407バイトのコードが13169バイトに増幅!

###ECMASCRIPT_2017 でCompileする
Copmpiler のオプションに(左側のjavascriptのコードを書く部分の先頭にあるコメント部分)に@language_out ECMASCRIPT_2017を追加します。Closure Compiler WebのUI上では設定できないのですが、書いちゃえばいいらしいです。UIで入力可能にしてくれればいいのに。。
image.png

無事、短くなりました。

Closure Compiler(Web)に貼るときに毎回書くことになるので、コードの先頭に書いちゃいましょう。javascriptのコメント形式なので動作には影響ないです。

ということで、最終形はこちらです。

fetch-sample-final.js
// ==ClosureCompiler==
// @output_file_name default.js
// @compilation_level SIMPLE_OPTIMIZATIONS
// @language_out ECMASCRIPT_2017
// ==/ClosureCompiler==
javascript:(
async function(){
    let URL = "https://search.rakuten.co.jp/search/mall/%E6%9C%AC/";
    let links = await getLinks(URL);
  
    //取ってきたリンクを楽しむ部分
    console.log('show link (%s)', links.length);
    links.forEach(function(v,i,ar){
        console.log('[%s] %s', i, v);
    });
  
    console.log('END OF SCRIPT');
  
      //HTMLを取りに行ってリンクを探す部分
      async function getLinks(url){
         
          console.log('start getLinks()');

          //fetchの結果を待つ
          console.log(' start fetch');
          let response = await fetch(url);
          console.log(' end fetch');
          console.log(response);

          //response.text()もawaitが必要
          console.log(' start response.text');
          let html =  await response.text();
          console.log(' end response.text');
          console.log(html);
          
          let regexp = /<a target="_top" href="([^"]*)" data-track-action="img">/g;
          let match;
          let matches = [];
          while ((match = regexp.exec(html))!== null) {
              matches.push(match[1]);
          }
          console.log('mathced %s', matches.length);

          console.log('End getLinks()');
          return matches;
      }
    }
    )();

#参考資料
https://developers.google.com/closure/compiler/docs/api-ref
defaultだとECMASCRIPT5なのですね。

https://github.com/google/closure-compiler-js/blob/master/README.md#flags
フラグの種類

https://github.com/google/closure-compiler/issues/3086
Closure Compiler Webでもlanguage_outputオプションを使えることを知ったチケット。$jscomp is not definedが出た場合もたぶんオプションの設定で回避可能です。

13
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?