bookmarkletでfetchとasyncとawait

#はじめに
過去に[ブックマークレットでjQuery使ってajaxをする](https://qiita.com/kanaxx/items/265aee04e479a5a40e51)記事を書きましたが、今時はjQueryを引っ張ってくることもなく標準装備の`fetch`をするのが主流っぽいので、ブックマークレットから`fetch`するサンプルです。

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

##普通のコード

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

```javascript
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

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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/8df04cb2-5b45-8070-10a7-fd2447b16b00.png)

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

###ECMASCRIPT_2017 でCompileする
Copmpiler のオプションに（左側のjavascriptのコードを書く部分の先頭にあるコメント部分）に```@language_out ECMASCRIPT_2017```を追加します。Closure Compiler WebのUI上では設定できないのですが、書いちゃえばいいらしいです。UIで入力可能にしてくれればいいのに。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/2753f394-4634-f769-6eff-280e147568d9.png)

無事、短くなりました。

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

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

```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```が出た場合もたぶんオプションの設定で回避可能です。