20210125のJavaScriptに関する記事は21件です。

Canvasのことをちょっぴり知る

この記事について

HTMLのCanvasで「どんなことができるのか?」「どんな作品ができるのか」というところを重点的にまとめた記事になります。技術的な内容にはあまり触れていないです。

対象者

  • Canvasを知らない人や触ったことがない人

Canvasとは?

HTML5から導入された図形を描くための技術仕様です。
JavascriptとHTMLの<canvas>を使用して図形を描くことができます。

Canvasを使った作品例だと以下のようなものがあります。

できること

基本的に2Dの図形を対象としていますが、3Dの図形も描けるので、アイディア次第では様々なことが表現できそうです:clap_tone1:
3Dの図形を描くときは Canvas APIではなくWebGL APIを使用して描きます。

2Dでできることは以下です。

  • 円や四角などの色々な形の図形の作成
  • それらを変形させる
  • グラデーションをつける
  • 画像との合成やクリッピング
  • アニメーションをつける(CSSのアニメーションではなくCanvasAPIで用意されてるアニメーション)

基本的な描画の流れ

  1. HTMLで<canvas>要素のwidthとheightとidを指定する
  2. getElementByIdメソッドで要素を取得して、getContextメソッドの引数に2Dを渡すと2Dの図形を描画するのに必要なメソッドやプロパティを持つオブジェクトが取得できます
  3. canvasAPIの機能を使用して図形を描く
  • widthとheightを指定しないとデフォルトでwidth:300pxheight:150pxとして設定されます。この枠の中がキャンバスとなって図形を描くスペースとなります。
  • 引数に webglを渡すと3Dの図形を描画できるオブジェクトが取得できます。
index.html
<canvas id="canvas"></canvas>
main.js
// DOMの中からcanvas要素を取得
const canvas = document.getElementById('canvas');
// 2Dの図形を描画するメソッドやプロパティを持ったオブジェクトを取得
const ctx = canvas.getContext('2d');
// 色を緑色に指定
ctx.fillStyle = 'green';
// 四角を描画(x, y, 幅, 縦)
ctx.fillRect(10, 10, 150, 100);

See the Pen eYdawGg by kena-nk (@kena-nk) on CodePen.

Canvasの基本的なアニメーション

CSSのアニメーション(@keyframesとか)とはちょっと異なります。
Canvasでのアニメーションは以下のように描画されます。

  1. 要素を削除
  2. 要素を描画
  3. 要素を動かす
  4. 終了でなければ1.に戻る

イメージとしてはパラパラ漫画のように画面が挿し変わっていく感じです。

再描画を行うループメソッドは以下3つを状況に応じて使い分けるみたいです↓

  • setInterval():一定時間ごとに特定の処理を繰り返す
  • setTimeout():一定時間後に特定の処理をおこなう
  • requestAnimationFrame():ブラウザ描画単位で呼びだされて、次の描画がされる前にアニメーションを実行する関数を呼び出す

See the Pen mdrZbQX by kena-nk (@kena-nk) on CodePen.

試しに作った作品

  • カーブを描く直線が描けるプロパティで作ったプリン


    See the Pen
    OJRYYBw
    by kena-nk (@kena-nk)
    on CodePen.


  • 円と直線を組み合わせて作ったドラえもん


    See the Pen
    WNGBBYP
    by kena-nk (@kena-nk)
    on CodePen.


おわりに

Canvasを初めて触ってみたの感想としては、アニメーション付けなければお絵かきしているような感覚で使えて楽しいな〜と思いました:raised_hands_tone1:

でも、ただ普通の丸を描くだけでも、x座標やy座標の指定だったり、Mathオブジェクトが頻繁に登場したりで数学が苦手な私的には辛みを感じたのですが。。。図形が完成していく過程は面白かったです!

今回は触っていないのですが、調べてみるとライブラリも充実していました!
実務で使うとしたらやはりライブラリを使用するんですかね?

以下MDNがまとめてくれているライブラリ一覧です?
https://developer.mozilla.org/ja/docs/Web/API/Canvas_API#libraries

興味がある方はぜひCanvasチュートリアルやってみてください!!では!!

おまけ

CanvasがHTML5に導入された経緯

Mac OS X v10.4の内部でWebKitコンポーネントとして、DashboardウィジェットやSafariでのアプリケーションを強化するために、2004年にアップルが最初に導入した。後に、Mozilla FirefoxやOperaでも採用され、WHATWGで、新しい標準規格として標準化された。

2009年の夏頃に、文字列描画のAPIとピクセル操作のAPIが追加になり、ウェブブラウザに実装された。

その後、HTML Canvas 2D Context, Level 2 が作られ、2012年12月17日に最初の W3C Working Draft が発表になった。

ソース:Wikipedia

要約すると、Appleが最初にDashboardウィジェットやSafariを良くしたくて取り入れたら、その良さに気づいた他のみんなも導入し始めて、最終的にHTML5入れちゃうか!って感じで追加されたのかなと思う

WHATWG(ワットダブルジー)とは?

HTMLと関連技術の開発をするためのコミュニティです!
元々はWeb技術の標準化を行う、World Wide Web(略称:W3C)の対抗組織だったみたいです。

HTML5の策定にあたってW3CとWHATWGは協力関係となり、WHATWGが提唱したものを元にW3Cが策定しました。
2019年からはWHATWGが随時更新していく「HTML Living Standard」をHTML標準と認め、W3Cが独自に規格を策定しないことなどが合意されました。

MDNが公式ドキュメントだと思ってたけどHTML Living Standardが公式ドキュメントみたいです。

参考サイト

Canvasアニメーションの要点
https://qiita.com/nekoneko-wanwan/items/33afa5d20264c83b2bd1

Canvasのイベント操作まとめ
https://qiita.com/nekoneko-wanwan/items/9af7fb34d0fb7f9fc870

Canvasチュートリアル
https://developer.mozilla.org/ja/docs/Web/API/Canvas_API/Tutorial

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript 関数の省略記法おさらい

引数を渡すと 1 足した数を返却する add 関数(以下)を例に説明する。

昔の基本系(未省略)

function add(x) {
  return x + 1;
}

補足:JavaScript (ECMAScript) は年々仕様がアップされて便利になっています。これは昔の基本系で、2015 年( ES2015 )からアロー関数という記法が仕様策定されていますので、アロー関数を使いましょう。アロー関数は省略の意味があるだけでなく、よりバグが混入しにくい仕組みを備えています。

1. 基本系(省略)

アロー関数化( ES2015+ )により function 文字列が省略可能

const add = (x) => {
  return x + 1;
};

2. 更成る省略

引数が 1 つの場合は引数の括弧が省略可能

const add = x => {
  return x + 1;
};

3. 更成る更成る省略

関数内処理が return のみの場合は return 文字列と波括弧が省略可能

const add = x => x + 1;

おわりに

超サイヤ人もこんな感じだったような。
JavaScript 書き慣れていきましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ElectronのcontextBridgeで値の受け渡し

はじめに

備忘録として適当にここに載せておきます。
ElectronのcontextBridgeを使い、下記のコードでデータ(JSON等)の受け渡し等が出来るのではないかと思います。
この記事はElectronを始めて2日目に書いた記事です。
間違っているかもしれません。

コード

main.js
/*レンダラープロセス側
任意の値をpreloadへ渡す
*/
console.log(pr_test.show_data);
var button = document.getElementById('button1');
   button.addEventListener("click",function(){
       var test = "TEEEEEEEEST";//任意の値
       pr_test.api.add_glist(test);//preloadへ渡す
   })
preload.js
//preload側
const fs = require('fs');
const { contextBridge, ipcRenderer} = require("electron");
contextBridge.exposeInMainWorld(
    "pr_test", {
      show_data: () => { return(fs.readFileSync(__dirname+'/json/test.json', 'utf8'))},//JSONをレンダラ側へ渡す
      add_data: (test) =>{new Notification(test);}//レンダラ側から受け取った値をOS側で通知する
    }
  );
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React Native] サーバーやDBからのデータ取得

0. 目的

Hooksを用いてサーバーやDBからデータを取得

1. 環境

  • React : 16.8.6
  • React Native : 0.63.4

2. ソースコード

GetData.js
import React from 'react';
import {FlatList, Text, View} from 'react-native';

import {useEffect, useState} from 'react';

function App() {
  const [response, setResponse] = useState([]);

  useEffect(() => {
    // データ取得(DBの場合もここでread処理を行う)
    fetch('https://zipcloud.ibsnet.co.jp/api/search?zipcode=7830060') // [1]
      .then((response) => response.json())
      .then((responseJson) => {
        setResponse(responseJson['results']);
      });
  }, []);

  if (!response.length) { // データ取得前
    return <Text>Loading...</Text>;
  } else if (response.length) { // データ取得後
    return (
        <FlatList
          data={response}
          renderItem={({item}) => (
            <View>
              <Text>{item.zipcode}</Text>
              <Text>
                {item.address1}
                {item.address2}
                {item.address3}
              </Text>
            </View>
          )}
          keyExtractor={(item) => item.zipcode}
        />
    );
  }
}

export default App;

3. 結果

2

「Loading」が表示された後、上の画像のように描画される.

4. 解説

処理の流れ
1. 「Loading」を表示
2. 「useEffect」内の処理を実行
3. 「住所」を表示

useEffectはレンダリング後に実行されるため、responseの有無により描画内容を変更する.
また、setResponseによりresponseの値が変更された際に再レンダリングされるため、データ取得後には再度if文が実行され、データ内容が表示される.

5. 参考

[1]郵便番号検索API

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

社会人2年目終盤になってやっとQiitaを始めてみる

始めるに至った経緯

社会人になって気づけば2年、会社の業務をこなすことにも慣れてきて自分のスキルアップをしたくなってきたので備忘録としてQiitaを始めてみる

Qiitaを使う目的

備忘録(今のところ)

専門

  1. 電気電子
  2. プログラミング(←仕事のメイン)
  3. 機構設計

今までやってきたこと

  • 電子工作
  • 電子回路設計
  • Python
  • JavaScript
  • C++

やりたいこと

  • 筋電取得による筋トレの負荷の取得
  • 自分の本棚を管理するアプリ(LINEなどと連携できるとなおよし)
  • 家の中のリモコンを携帯から操作可能にする
  • 他にもいろいろ

目標

1週間に1回は記事を書く

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript Promiseの実行タイミング Promise.start() とか Promise.execute() 的なもの

Promiseには .start() .execute() が存在しない

Promise objectを作ってtaskを設置したとき、発火方法がわからなかった。

イメージとしては ↓ だったんだけど

const task = new Promise(()=>{ // task});
task.start() // こんなものはない

Promiseにそんなmethodはなかった。使えるのは6つだけ。

  1. Promise.all(promises)
  2. Promise.allSettled(promises)
  3. Promise.race(promises)
  4. Promise.any(promises)
  5. Promise.resolve(value)
  6. Promise.reject(error)

https://javascript.info/promise-api

Promiseは Object を作った瞬間に start() するものだった!!

  1. 関数の中なら、関数が呼ばれると、Promise objectが生成される = 実行される
  2. MAIN関数や <script> の直下なら file loadした瞬間に Promise objectが生成される = 即実行される

発火のコントロール方法

まずPromiseの宣言方法(というか、使い方) は2つ。

  1. Object(変数)として作る
  2. 関数の中に作る(これを個人的には 関数として作る と言いたい)

↓では最初の3つ(a, b, c)が変数、残りが関数(d, e) で書いている。

<script>

// awaitを中で使うので、この start() は async で宣言する
const start = async () => {

    //////////////////////////////////////////////////////
    // (a) objectだと宣言した瞬間に実行される
    const objImmediately = new Promise((resolve) => {
        resolve();
        console.log("(a) objImmediately done");
    });

    // (b) objectだと宣言した瞬間に実行される
    const objImmediatelyWithSleep = new Promise((resolve) => {
        setTimeout(()=>{
            resolve();
            console.log("(b) objImmediatelyWithSleep done");
        }, 400)
    });

    // (c) object宣言とともに実行 & awaitでこれより下の行を一時停止する
    const objImmediatelyAndAwait = await new Promise(async (resolve) => {
        await setTimeout(async ()=>{
            resolve();
            console.log("(c) objImmediatelyAndAwait done");
        }, 300)
    });


    // (d) 関数の中で Promise objectを作る。生成即実行かと思いきや、関数が呼ばれるまでは実行されない。
    const funcLater = async () => {
        await new Promise(async (resolve) => {
            setTimeout(()=>{
                resolve();
                console.log("(d) funcLater done");
            }, 200)
        });
    }

    // (e) これも関数の中なので即実行されない。Promiseをreturnすると then が使える
    const funcLaterReturnPromise = async () => {
        return await new Promise((resolve) => {
            setTimeout(()=>{
                resolve();
                console.log("(e) funcLaterReturnPromise done");
            }, 100)
        });
    }


    // MAIN ////////////////////////////////////////////////////

    // (a, b, c) objectは関数ではないのでできない & 不要
    // objImmediately()
    // objImmediatelyWithSleep()
    // objImmediatelyAndAwait()


    // (d) 関数を呼ぶ = promiseが初めて実行される
    await funcLater();

    // 返り値がないので then はできない
    // await funcLater().then(()=>{console.log("then")});
    // Uncaught (in promise) TypeError: Cannot read property 'then' of undefined

    // (e) 返り値がpromiseだとチェーンできる
    await funcLaterReturnPromise()
    .then(
        ()=>{console.log("(e) then")
    });

    console.log("(f) Finished.");
}

start();
</script>

近頃は猫も杓子も promise をreturnしてくるので、async関数じゃないとコールすらできなかったりする。
(start()関数がわざわざ async function になってるのは中で awaitを使ってるから)

result

(a) objImmediately done
(c) objImmediatelyAndAwait done
(b) objImmediatelyWithSleep done
(d) funcLater done
(e) funcLaterReturnPromise done
(e) then
(f) Finished.

発火順 と 完了順

x = 発火
o = 実行中

image.png

objectの3つは宣言 即 実行されている。

関数の2つはその後で call されている。

発火順は上から下に a-e だ。
だけど、awaitが絡んで完了順は C が B を追い越している。その他は順番通り。
肝は、d&eのsleepがb&cより短いのに、d&eはb&cより後に完了する点だ。これは単純に発火を遅らせて実現している。

(c) がobjectだけどawaitしているので、(d)は (c) が終わるまで待っている。

(e) は (d) が終わるまで待っている。

まとめ

  1. Promise を objectとしてコールすると、即実行される。
  2. Promiseの発火タイミングをコントロールしたかったら、関数の中で宣言する。 関数をいつ呼ぶか でコントロールする。

直接 new Promise してるコードを見たら その関数がいつ呼ばれたか を知らないと即実行されたタイミングがわからない。外側の関数を見に行く必要がある。しかし大概そこでも new Promiseされている。さらに外側の関数を見に行くことになる。

僕らは無限のPromise地獄の中でもがいているのだ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Photoshop Script(JSX)を使ってレイヤーごとに保存する

はじめに

普段photoshopを使用している中で「レイヤーごとに書き出し」が不便だと思ったので、勉強がてらPhotoshop Scriptを書いてみました。
既存の素晴らしいものがあるとは思いますが、個人的に必要最低限のシンプルなものがほしかったので簡潔なものになってます。
備忘録的位置づけで簡単な説明を添えてこちらにおいておきます。

機能&操作

・常に表示させたいレイヤーはロックしておく(背景や上に乗せるロゴなど共通のもの)
・ロックされているレイヤーは無視して、順番にレイヤーを書き出していく

コード全文

const saveFolderPath = Folder.selectDialog("フォルダを選択してください");

function divideVisibleBylocked(){
    for (i=0; i < activeDocument.layers.length; i++){
        var activeLayer = activeDocument.layers[i]
        if(activeLayer.allLocked == true){
            activeLayer.visible = true;
        }else{
            activeLayer.visible = false;
        }
    }
}

function allLayerSave(){
    for (i=0; i < activeDocument.layers.length; i++){
        var activeLayer = activeDocument.layers[i]
        if(activeLayer.allLocked == false){
            var layerName = activeLayer.name;
            var fileName = layerName + ".jpg";
            var filePath = saveFolderPath+ "/" + fileName;
            activeLayer.visible = true;
            saveJpegImage(filePath);
            activeLayer.visible = false;
        }
    }
}

divideVisibleBylocked()
allLayerSave()


// --------------------------------------------
function saveJpegImage(filePath){
    fileObj = new File(filePath);
    jpegOpt = new JPEGSaveOptions();
    jpegOpt.embedColorProfile = true;
    jpegOpt.quality = 12;
    jpegOpt.formatOptions = FormatOptions.PROGRESSIVE;
    jpegOpt.scans = 3;
    jpegOpt.matte = MatteType.NONE;
    activeDocument.saveAs(fileObj, jpegOpt, true, Extension.LOWERCASE);
}

保存先指定

const saveFolderPath = Folder.selectDialog("フォルダを選択してください");

ロックされているレイヤーはすべて表示、されてないレイヤーはすべて非表示に

function divideVisibleBylocked(){
    for (i=0; i < activeDocument.layers.length; i++){
        var activeLayer = activeDocument.layers[i]
        if(activeLayer.allLocked == true){
            activeLayer.visible = true;
        }else{
            activeLayer.visible = false;
        }
    }
}

ロックされているレイヤ以外を1つずつ表示して書き出し

function allLayerSave(){
    for (i=0; i < activeDocument.layers.length; i++){
        var activeLayer = activeDocument.layers[i]
        if(activeLayer.allLocked == false){
            var layerName = activeLayer.name;
            var fileName = layerName + ".jpg";
            var filePath = saveFolderPath+ "/" + fileName;
            activeLayer.visible = true;
            saveJpegImage(filePath);
            activeLayer.visible = false;
        }
    }
}

jpgでの書き出し

function saveJpegImage(filePath){
    fileObj = new File(filePath);
    jpegOpt = new JPEGSaveOptions();
    jpegOpt.embedColorProfile = true;
    jpegOpt.quality = 12;
    jpegOpt.formatOptions = FormatOptions.PROGRESSIVE;
    jpegOpt.scans = 3;
    jpegOpt.matte = MatteType.NONE;
    activeDocument.saveAs(fileObj, jpegOpt, true, Extension.LOWERCASE);
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webpackとは?

何度も調べまわっているような気がしたので、関連の用語をまとめてメモしてみました。
修正点・誤っている箇所等あれば、アドバイスを頂けましたら幸いです。

Webpackとは

JavaScriptのモジュールバンドラー。
ややこしいと思ったのはWebpackerとは別物だということ。
(Webpackerは、Webpackを使用してRails上でJavaScript開発をするために必要な一連のまとまりを、標準で実装することができるgemパッケージ)

モジュールとは

ひとつのファイル。
関数やコンポーネント等、機能ごとに分割したファイルのこと。

※パッケージとは

複数のモジュールをグループ化したもののこと。

※ライブラリとは

いくつかのパッケージをまとめて、ひとつのライブラリとしてインストールできるようにしたもの。

モジュールバンドラー(module bundler)とは

モジュールをひとまとめにするツール。
まとめることによってHTTPリクエストの数を減らし、表示速度の高速化に繋げるというのが主な理由のよう。
それならば、初めから一つのファイルに記述すれば良いのでは。とつい自分も考えてしまったのですが、

①コード量が増えてきたら、どこに何を書いたのか探すのが大変であること
②名前空間の汚染が起こる可能性(誤って同じ変数を使用して上書きしてしまったり)
③以上よりメンテナンスがしづらい

などの理由から、基本はひとつの処理や機能(関数、コンポーネント)をひとつのファイルに書き、他のファイルでそのモジュールが必要になったら、都度読みこんで利用する。

おわりに

Webpackerが難解に思われるのは、モジュールのバンドル以外の処理の代替を行っていることが大きな理由のひとつとしてあるようです。一歩ずつ理解していきたいと思っています。

参考記事

『Webpackってどんなもの?』
https://qiita.com/kamykn/items/45fb4690ace32216ca25
『webpackとは?』
https://qiita.com/minato-naka/items/0db285f4a3ba5adb6498
『最新版で学ぶwebpack 5入門JavaScriptのモジュールバンドラ』
https://ics.media/entry/12140/
『【基本】webpackerとは何か学ぼう』
https://it-kyujin.jp/article/detail/1661/
『はじめてのモジュールバンドラー』
https://blog.mach3.jp/2016/10/01/module-bundler.html
『JavaScriptにおけるモジュールとimport/exportの使い方』
https://analogic.jp/module-summary/
『モジュールバンドラーはなぜモダンなフロントエンドの開発に必要なのか?』
https://note.com/billion_dollars/n/n596fecfdeb2e

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript Promise, await, Promise.all() 一部処理だけ待ちたい/同期させたい/他は即実行させたい場合

Promise.all系の特徴

列挙されているtaskは必ず一気に実行されてしまう。
Aが成功したときだけBに行く、という処理はできない。
そうしたい場合の選択肢は4つ

  1. そもそもAとBがヒトツナギなら、BをAの中に書けばいいやん
  2. 全部一気に実行 → 各taskの中で、同期したいtaskの完了をpollingして待つ。
  3. ひとつずつ実行する。Aが成功してたらBも実行、というように。
  4. 同期したいのはawaitで(3)ぽく、非同期で一気に行きたいのは(2)風に

この記事はこの中の 2 と 3 を扱う。実際のコーディングでは 4 を採用したが、2+3で4ができる仕組み。

(a) 関数の外の値が書き換わるまで待って続行

<script>
let updated = 0;

apiMock = (i) => {
    return new Promise((resolve) => {
        setTimeout(function(){
            updated = i;
            resolve(i);
        }, i);
    });
}

async function start() {
    res = await apiMock(1000);
    console.log(`apiMock is done. res=${res}`);
    console.log(`updated=${updated}`);
}

start();


</script>

result

console.logがapiMock()が終わるまで(updatedが1000になるまで) 待ったことがわかる

apiMock is done. res=1000
updated=1000

(b) Promise.all() の中で一部のpromiseだけ同期実行したい(待たせたい、遅らせたい)

apiMock()は処理が終わるまで時間がかかる。終わったら updated flagを書き換える。
apiWait()は即実行されるが、そのフラグが変わるまで待つ。

<script>
let updated = 0;

// Task1: Wait 1 sec
apiMock = new Promise((resolve) => {
    setTimeout(function(){
        updated = 1500;
        resolve(1500);
        console.log("apiMock done");
    }, 1500);
});

// Task2: Wait 0 sec
apiNoWait = new Promise((resolve) => {
    resolve(1000);
    console.log("apiNoWait done");
});

// Task3: Wait until task1 is done
apiWait = new Promise((resolve, reject) => {
    let maxTry = 0;

    const interval = setInterval(async () => {
        if (maxTry++ > 100) { // give up after x tries
            reject(`api does not respond`);
            clearInterval(interval);
        } else if (updated > 0) {
            resolve(updated);
            clearInterval(interval);
            resolve(100);
            console.log("apiWait done");
        } else {
            console.log(`apiWait had waited ${maxTry} times ...`);
        }
    }, 300);
});


// 不要。new Promiseだけで走り出す。
// () => Promise.all([apiMock]);

/* もし すべて終わった後に処理をしたければ then する

Promise.all([apiMock, apiNoWait, apiWait]).then((p) => {
    console.log("finished");
    console.log(p);  // 全taskの戻り値(resolveの引数)が帰る →  [1500, 1000, 1500]
});

*/

</script>

result

had waited n times... で apiWait() が待ったことがわかる。

VM129:21 apiNoWait done
Promise {<pending>}
VM129:38 apiWait had waited 1 times ...
VM129:38 apiWait had waited 2 times ...
VM129:38 apiWait had waited 3 times ...
VM129:38 apiWait had waited 4 times ...
VM129:14 apiMock done
VM129:36 apiWait done

(c) 開始タイミングを完璧に同期的にしたい

apiMock()の中の new Promise も await + asyncにしないと同期しないのがめんどくさい。

<script>
let updated = 0;

// Task1: Wait 1 sec
apiMock = async () => {
    await new Promise(async (resolve) => {
        setTimeout(function(){
            updated = 1500;
            resolve(1500);
            console.log("apiMock done");
        }, 1500);
    });
}

// Task2: Wait 0 sec
apiNoWait = async () => {
    await new Promise(async (resolve) => {
        resolve(1000);
        console.log("apiNoWait done");
    });
}

// Task3: Wait until task1 is done
apiWait = async () => {
    await new Promise(async (resolve, reject) => {
        let maxTry = 0;

        const interval = setInterval(async () => {
            if (maxTry++ > 100) { // give up after x tries
                reject(`api does not respond`);
                clearInterval(interval);
            } else if (updated > 0) {
                resolve(updated);
                clearInterval(interval);
                resolve(100);
                console.log("apiWait done");
            } else {
                console.log(`apiWait had waited ${maxTry} times ...`);
            }
        }, 300);
    });
}

const start = async () => {
    await apiMock();
    await apiNoWait();
    await apiWait();
}

start();
</script>

result

apiMockが終わった後にapiWait()が走るので、 待ってます... messageが出ない。

apiMock done
apiNoWait done
apiWait done

(d) promiseのひとつが fail したときの処理

この3連続awaitにおいて

await apiMock();
await apiNoWait();
await apiWait();

例えば apiMock() がエラーで終わると、残りは実行されない

Uncaught Error: apiMock is failed.
    at <anonymous>:13:13
// あとのawaitが走らない

(e) エラーだと then が実行されない Promise.all

これをやると一気にスタートしてしまうので同期を含みたい場合は内部で待つ処理が必要。

ひとつでもrejectされると全体が thenが実行されない( finished が出ることがない)

<script>
let updated = 0;

// Task1: Wait 1 sec
apiMock = new Promise((resolve) => {
    throw new Error("api failed");       // <--------- エラー
);

// Task2: Wait 0 sec
apiNoWait = new Promise((resolve) => {
        resolve(1000);
        console.log("apiNoWait done");
    });

// Task3: Wait until task1 is done
apiWait = new Promise((resolve, reject) => {
        let maxTry = 0;

        const interval = setInterval(() => {
            if (maxTry++ > 50) { // give up after x tries
                reject(`api does not respond`);
                clearInterval(interval);
            } else if (updated > 0) {
                resolve(updated);
                clearInterval(interval);
                resolve(100);
                console.log("apiWait done");
            } else {
                console.log(`apiWait had waited ${maxTry} times ...`);
            }
        }, 300);
    });

Promise.all([apiMock, apiNoWait, apiWait]).then((p) => {
    console.log("finished");
    console.log(p);
}).catch((p) => {
    console.log("Found an error:");
    console.log(p);
});


</script>

result

thenが走らないのでfinishedが出ません。でも、 全taskがkickされてます。
最後にcatchがまた走るわけでもない。rejectされた瞬間にcatchが走る。

apiNoWait done
index.html:48 Found an error:
index.html:49 Error: api failed
    at index.html:11
    at new Promise (<anonymous>)
    at index.html:10
index.html:39 apiWait had waited 1 times ...
index.html:39 apiWait had waited 2 times ...
index.html:39 apiWait had waited 3 times ...
...
index.html:39 apiWait had waited 50 times ...
index.html:39 apiWait had waited 51 times ...

(f) エラーでもthenを実行させる Promise.allSettled().then()

<script>
let updated = 0;

// Task1: Wait 1 sec
apiMock = new Promise((resolve) => {
    throw new Error("api failed"); // <--------- エラー
);

// Task2: Wait 0 sec
apiNoWait = new Promise((resolve) => {
        resolve(1000);
        console.log("apiNoWait done");
    });

// Task3: Wait until task1 is done
apiWait = new Promise((resolve, reject) => {
        let maxTry = 0;

        const interval = setInterval(() => {
            if (maxTry++ > 50) { // give up after x tries
                reject(`api does not respond`);
                clearInterval(interval);
            } else if (updated > 0) {
                resolve(updated);
                clearInterval(interval);
                resolve(100);
                console.log("apiWait done");
            } else {
                console.log(`apiWait had waited ${maxTry} times ...`);
            }
        }, 300);
    });

Promise.allSettled([apiMock, apiNoWait, apiWait]).then((p) => {
    console.log("finished");
    console.log(p);
}).catch((p) => {
    console.log("Found an error:");
    console.log(p);
});


</script>

awaitと同じ。一気にスタートしてしまうのは Promise.allSetteled と同じ。

result

最後の戻り値の中で、エラー(reject)が有ったかどうかを確認できる。 p.reasonがあるとrejectされている。

apiNoWait done
index.html:39 apiWait had waited 1 times ...
index.html:39 apiWait had waited 2 times ...
...
index.html:39 apiWait had waited 51 times ...
index.html:45 finished
index.html:46 (3) [{…}, {…}, {…}]
0: {status: "rejected", reason: Error: api failed at file:///Users/JP23223/Desktop/index.html:11:8 at new Promise (<anonymo…}
1: {status: "fulfilled", value: 1000}
2: {status: "rejected", reason: "api does not respond"}

(g) awaitで同期しながら、エラー影響があるものだけ実行しない

apiMockが成功しない限りapiWaitは実行したくないので、セットでtryさせる。
apiNoWaitは関係ないので、apiMockが失敗しても実行される。

<script>
// Task1: Wait 1 sec
apiMock = async () => {
    throw new Error("apiMock is failed"); // <-------- エラー
}

// Task2: Wait 0 sec
apiNoWait = async () => {
    await new Promise(async (resolve) => {
        resolve(1000);
        console.log("apiNoWait done");
    });
}

// Task3: Wait until task1 is done
apiWait = async () => {
    console.log("apiWait done");   // <----- apiMockを待つ処理が不要になる
}

const start = async () => {
    try {
        await apiMock();
        await apiWait();
    } catch(e) {
        console.error(e);
    }
    await apiNoWait();
}

start();
</script>

result

apiWaitが実行されていない。

index.html:30 Error: apiMock is failed
index.html:16 apiNoWait done

まとめ

awaitが動かなくなったら、目の前にあるすべての ()async() にしよう!!!!
目の前にあるすべての関数の前に await をつけよう!!! await setTimeout(async()) にするんだぞ!
めんどくさいぞ!!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript Promise, await, Promise.all() 周りのメモ。一部処理だけ待ちたい/同期させたい場合 etc

Promise.all系の特徴

列挙されているtaskは必ず一気に実行されてしまう。
Aが成功したときだけBに行く、という処理はできない。
そうしたい場合の選択肢は4つ

  1. そもそもAとBがヒトツナギなら、BをAの中に書けばいいやん
  2. 全部一気に実行 → 各taskの中で、同期したいtaskの完了をpollingして待つ。
  3. ひとつずつ実行する。Aが成功してたらBも実行、というように。
  4. 同期したいのはawaitで(3)ぽく、非同期で一気に行きたいのは(2)風に

この記事はこの中の 2 と 3 を扱う。実際のコーディングでは 4 を採用したが、2+3で4ができる仕組み。

(a) 関数の外の値が書き換わるまで待って続行

<script>
let updated = 0;

apiMock = (i) => {
    return new Promise((resolve) => {
        setTimeout(function(){
            updated = i;
            resolve(i);
        }, i);
    });
}

async function start() {
    res = await apiMock(1000);
    console.log(`apiMock is done. res=${res}`);
    console.log(`updated=${updated}`);
}

start();


</script>

result

console.logがapiMock()が終わるまで(updatedが1000になるまで) 待ったことがわかる

apiMock is done. res=1000
updated=1000

(b) Promise.all() の中で一部のpromiseだけ同期実行したい(待たせたい、遅らせたい)

apiMock()は処理が終わるまで時間がかかる。終わったら updated flagを書き換える。
apiWait()は即実行されるが、そのフラグが変わるまで待つ。

<script>
let updated = 0;

// Task1: Wait 1 sec
apiMock = new Promise((resolve) => {
    setTimeout(function(){
        updated = 1500;
        resolve(1500);
        console.log("apiMock done");
    }, 1500);
});

// Task2: Wait 0 sec
apiNoWait = new Promise((resolve) => {
    resolve(1000);
    console.log("apiNoWait done");
});

// Task3: Wait until task1 is done
apiWait = new Promise((resolve, reject) => {
    let maxTry = 0;

    const interval = setInterval(async () => {
        if (maxTry++ > 100) { // give up after x tries
            reject(`api does not respond`);
            clearInterval(interval);
        } else if (updated > 0) {
            resolve(updated);
            clearInterval(interval);
            resolve(100);
            console.log("apiWait done");
        } else {
            console.log(`apiWait had waited ${maxTry} times ...`);
        }
    }, 300);
});


// 不要。new Promiseだけで走り出す。
// () => Promise.all([apiMock]);

/* もし すべて終わった後に処理をしたければ then する

Promise.all([apiMock, apiNoWait, apiWait]).then((p) => {
    console.log("finished");
    console.log(p);  // 全taskの戻り値(resolveの引数)が帰る →  [1500, 1000, 1500]
});

*/

</script>

result

had waited n times... で apiWait() が待ったことがわかる。

VM129:21 apiNoWait done
Promise {<pending>}
VM129:38 apiWait had waited 1 times ...
VM129:38 apiWait had waited 2 times ...
VM129:38 apiWait had waited 3 times ...
VM129:38 apiWait had waited 4 times ...
VM129:14 apiMock done
VM129:36 apiWait done

(c) 開始タイミングを完璧に同期的にしたい

apiMock()の中の new Promise も await + asyncにしないと同期しないのがめんどくさい。

<script>
let updated = 0;

// Task1: Wait 1 sec
apiMock = async () => {
    await new Promise(async (resolve) => {
        setTimeout(function(){
            updated = 1500;
            resolve(1500);
            console.log("apiMock done");
        }, 1500);
    });
}

// Task2: Wait 0 sec
apiNoWait = async () => {
    await new Promise(async (resolve) => {
        resolve(1000);
        console.log("apiNoWait done");
    });
}

// Task3: Wait until task1 is done
apiWait = async () => {
    await new Promise(async (resolve, reject) => {
        let maxTry = 0;

        const interval = setInterval(async () => {
            if (maxTry++ > 100) { // give up after x tries
                reject(`api does not respond`);
                clearInterval(interval);
            } else if (updated > 0) {
                resolve(updated);
                clearInterval(interval);
                resolve(100);
                console.log("apiWait done");
            } else {
                console.log(`apiWait had waited ${maxTry} times ...`);
            }
        }, 300);
    });
}

const start = async () => {
    await apiMock();
    await apiNoWait();
    await apiWait();
}

start();
</script>

result

apiMockが終わった後にapiWait()が走るので、 待ってます... messageが出ない。

apiMock done
apiNoWait done
apiWait done

(d) promiseのひとつが fail したときの処理

この3連続awaitにおいて

await apiMock();
await apiNoWait();
await apiWait();

例えば apiMock() がエラーで終わると、残りは実行されない

Uncaught Error: apiMock is failed.
    at <anonymous>:13:13
// あとのawaitが走らない

(e) エラーだと then が実行されない Promise.all

これをやると一気にスタートしてしまうので同期を含みたい場合は内部で待つ処理が必要。

ひとつでもrejectされると全体が thenが実行されない( finished が出ることがない)

<script>
let updated = 0;

// Task1: Wait 1 sec
apiMock = new Promise((resolve) => {
    throw new Error("api failed");       // <--------- エラー
);

// Task2: Wait 0 sec
apiNoWait = new Promise((resolve) => {
        resolve(1000);
        console.log("apiNoWait done");
    });

// Task3: Wait until task1 is done
apiWait = new Promise((resolve, reject) => {
        let maxTry = 0;

        const interval = setInterval(() => {
            if (maxTry++ > 50) { // give up after x tries
                reject(`api does not respond`);
                clearInterval(interval);
            } else if (updated > 0) {
                resolve(updated);
                clearInterval(interval);
                resolve(100);
                console.log("apiWait done");
            } else {
                console.log(`apiWait had waited ${maxTry} times ...`);
            }
        }, 300);
    });

Promise.all([apiMock, apiNoWait, apiWait]).then((p) => {
    console.log("finished");
    console.log(p);
}).catch((p) => {
    console.log("Found an error:");
    console.log(p);
});


</script>

result

thenが走らないのでfinishedが出ません。でも、 全taskがkickされてます。
最後にcatchがまた走るわけでもない。rejectされた瞬間にcatchが走る。

apiNoWait done
index.html:48 Found an error:
index.html:49 Error: api failed
    at index.html:11
    at new Promise (<anonymous>)
    at index.html:10
index.html:39 apiWait had waited 1 times ...
index.html:39 apiWait had waited 2 times ...
index.html:39 apiWait had waited 3 times ...
...
index.html:39 apiWait had waited 50 times ...
index.html:39 apiWait had waited 51 times ...

(f) エラーでもthenを実行させる Promise.allSettled().then()

<script>
let updated = 0;

// Task1: Wait 1 sec
apiMock = new Promise((resolve) => {
    throw new Error("api failed"); // <--------- エラー
);

// Task2: Wait 0 sec
apiNoWait = new Promise((resolve) => {
        resolve(1000);
        console.log("apiNoWait done");
    });

// Task3: Wait until task1 is done
apiWait = new Promise((resolve, reject) => {
        let maxTry = 0;

        const interval = setInterval(() => {
            if (maxTry++ > 50) { // give up after x tries
                reject(`api does not respond`);
                clearInterval(interval);
            } else if (updated > 0) {
                resolve(updated);
                clearInterval(interval);
                resolve(100);
                console.log("apiWait done");
            } else {
                console.log(`apiWait had waited ${maxTry} times ...`);
            }
        }, 300);
    });

Promise.allSettled([apiMock, apiNoWait, apiWait]).then((p) => {
    console.log("finished");
    console.log(p);
}).catch((p) => {
    console.log("Found an error:");
    console.log(p);
});


</script>

awaitと同じ。一気にスタートしてしまうのは Promise.allSetteled と同じ。

result

最後の戻り値の中で、エラー(reject)が有ったかどうかを確認できる。 p.reasonがあるとrejectされている。

apiNoWait done
index.html:39 apiWait had waited 1 times ...
index.html:39 apiWait had waited 2 times ...
...
index.html:39 apiWait had waited 51 times ...
index.html:45 finished
index.html:46 (3) [{…}, {…}, {…}]
0: {status: "rejected", reason: Error: api failed at file:///Users/JP23223/Desktop/index.html:11:8 at new Promise (<anonymo…}
1: {status: "fulfilled", value: 1000}
2: {status: "rejected", reason: "api does not respond"}

(g) awaitで同期しながら、エラー影響があるものだけ実行しない

apiMockが成功しない限りapiWaitは実行したくないので、セットでtryさせる。
apiNoWaitは関係ないので、apiMockが失敗しても実行される。

<script>
// Task1: Wait 1 sec
apiMock = async () => {
    throw new Error("apiMock is failed"); // <-------- エラー
}

// Task2: Wait 0 sec
apiNoWait = async () => {
    await new Promise(async (resolve) => {
        resolve(1000);
        console.log("apiNoWait done");
    });
}

// Task3: Wait until task1 is done
apiWait = async () => {
    console.log("apiWait done");   // <----- apiMockを待つ処理が不要になる
}

const start = async () => {
    try {
        await apiMock();
        await apiWait();
    } catch(e) {
        console.error(e);
    }
    await apiNoWait();
}

start();
</script>

result

apiWaitが実行されていない。

index.html:30 Error: apiMock is failed
index.html:16 apiNoWait done
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript: 型変換を利用した簡易的な書き方

数値型から文字列への変換

足し算は文字列連結の可能性と数値計算の可能性があるが、前者とみなされる。
const num = 999;
console.log(typeof num); //number
console.log(typeof num.toString()); //string
console.log(typeof (num + '')); //string

文字列から数値型への変換

引き算によって数値型に暗黙的に型変換される(文字列連結の可能性はないため?)
const str = '1000';
console.log(typeof str); //string
console.log(typeof Number(str)); //number
console.log(typeof parseInt(str)); //number
console.log(typeof +str); //number
console.log(typeof (str - 0)); //number
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのtruethyとfalsy

Truthy or Falsy

  • Booleanコンテキストに現れた時に true とみなされる値=truthy
  • Booleanコンテキストに現れた時に false とみなされる値=falsy
  • falsyのものは以下のもののみ。
    • false
    • 0
    • -0
    • 0n
    • "" (空文字)
    • null
    • undefined
    • NaN(Not a Number)

https://developer.mozilla.org/ja/docs/Glossary/Truthy

確認

console.log(Boolean(false)); //false
console.log(Boolean(0)); //false
console.log(Boolean(-0)); //false
console.log(Boolean(0n)); //false
console.log(Boolean("")); //false
console.log(Boolean(undefined)); //false
console.log(Boolean(NaN)); //false

これ以外のものは全部truthyである。例えば、すごく奇妙なことではあるが、以下の結果はtrueである。

console.log(Boolean(new Boolean(false))); //true

利用例

文字列が空であるかどうかのチェック
function helloname(name) {
    //nameが空であるかどうかのチェック
    if (!name) {
        name = 'Taro';
    }

    console.log("Hello " + name)
}
helloname();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript: truthyとfalsy、およびショートサーキット評価

Truthy or Falsy

  • Booleanコンテキストに現れた時に true とみなされる値=truthy
  • Booleanコンテキストに現れた時に false とみなされる値=falsy
  • falsyのものは以下のもののみ。
    • false
    • 0
    • -0
    • 0n
    • "" (空文字)
    • null
    • undefined
    • NaN(Not a Number)

https://developer.mozilla.org/ja/docs/Glossary/Truthy

確認

console.log(Boolean(false)); //false
console.log(Boolean(0)); //false
console.log(Boolean(-0)); //false
console.log(Boolean(0n)); //false
console.log(Boolean("")); //false
console.log(Boolean(undefined)); //false
console.log(Boolean(NaN)); //false

これ以外のものは全部truthyである。例えば、すごく奇妙なことではあるが、以下の結果はtrueである。

console.log(Boolean(new Boolean(false))); //true

利用例1:変数チェック

文字列が空であるかどうかのチェック
function helloname(name) {
    //nameが空であるかどうかのチェック
    if (!name) {
        name = 'Taro';
    }

    console.log("Hello " + name)
}
helloname();

利用例2:変数チェック+代入を一気にやる方法(ショートサーキット評価)

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_Operators

論理演算子は左から右へ評価されるため、論理演算子で左辺を評価した時点で論理式の結果が確定した場合には右辺の評価を行わないことを、ショートサーキット評価といいます。例えば、A && Bという論理式があった場合、Aがfalseなら、その時点で式全体の結果はfalseで確定するため、Bがどうであるかについてはチェックしません。:

valがtruthyならそのまま代入。valがfalsyなら評価を終了して右項に移動。
let val = "";
let x = val || "Bob";
console.log(x); //Bob
エラーがあればエラーメッセージを表示
error = new Error('foo');
console.log(error && error.message); //foo
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ウェブ開発者は必ず入れたいブラウザ拡張機能13選

本記事は、Simon Holdorf氏による「13 Must-Have Browser Extensions for Web Developers」(2021年1月5日公開)の和訳を、著者の許可を得て掲載しているものです。

ウェブ開発者は必ず入れたいブラウザ拡張機能13選

はじめに

Google Chromeなどの最新のブラウザは、ウェブサイトを訪問する時に楽しませてくれるだけでなく、アプリケーション構築のための素晴らしいツールをウェブ開発者に提供してくれます。たくさんの優れたブラウザ拡張機能の中から選べます。ソフトウェアエンジニアの生産性を高め、アプリケーションの開発を高速にし、バグの発見に役立つものを集めました。

React Developer Tools / Redux DevTools / Vue.js devtools

ex1

React.jsとVue.jsは素晴らしいJavaScriptフレームワークですが、その機能をフル活用するには、それぞれのブラウザ拡張機能を使用する必要があります。

React DeveloperToolsはChromeで利用できます。DevToolsを拡張し、Reactコンポーネント、props、状態を調べられます。React.js開発にすごく便利です!

アプリケーションの状態で困った経験があるなら、Redux DevToolsが役に立ちます。あらゆるアーキテクチャの状態に対応していて、特にReduxとの相性は抜群です。プロのヒント:Chromeで拡張機能を有効にして、Airbnb.comなどのウェブサイトを見てみてください。AirbnbはReact/Reduxを使っているのですが、どのように状態管理を構築しているかDevToolsで分かります。

かっこいいでしょう?

ex2

Vue.js devtoolsは、React Developer ToolsのVue版です。Vueコンポーネントのデバッグにすごく便利で、Vue開発者には必要不可欠です。

JSONView

ex3
JSONデータが他のブラウザではきれいに見えるのに、自分のブラウザではそう見えないのはなぜだろうと思ったことはありませんか?それはおそらく、Chrome用のJSONViewを使っているからです。この拡張機能は、本当にきれいに、ほぼ瞬時にJSONをフォーマットしてくれます。これは間違いなく、私のお気に入りの拡張機能の1つです。

Library Sniffer

ex4

Library Snifferは、ウェブアプリケーションが実行されているフレームワークやライブラリを素早く見つけたい場合に最適なツールです。例えば、ウェブサイトがWordPressやDrupalをベースにしているかどうか、React.js、Vue.js、AngularJSなどで構築されているかを検出できます。

CSS Scan 2.0

ex5

CSS Scan 2.0は、要素の上にカーソルを合わせるとCSSの検査やコピーができる、素晴らしい拡張機能です。ただし、ライセンスの購入が必要です。

代わりに無料で使えるものにはPeeper CSSがあります。これで、コードに手を焼くことではなくデザインに集中できます。

Web Developer

ex6

Web Developerは、さまざまな開発者向けツールをまとめたツールバーをブラウザに追加します。JavaScriptを無効にしたり、ページのレイアウトを変更したり、画像を操作したり、いろいろなことができます。ChromeとFirefoxで利用できます!

WhatFont

ex7

WhatFontは、ウェブサイトで使用しているフォントを識別する小さな拡張機能です。拡張機能を有効にして、テキストの上にカーソルを合わせるだけです。それくらい簡単です!ChromeとFirefoxで利用できます。

ColorZilla

ex8

ColorZillaはChromeとFirefoxで利用できます。高度なスポイト、カラーピッカー、グラデーションジェネレータなどの機能が使えます。例えば、ウェブサイト上のピクセルの色を取得できます。色の履歴やパレットビューアもあります。

Spectrum

ex9

アクセシビリティは現代のウェブ開発で大切なファクターです。ウェブサイトやアプリケーションの使用にハンディがある人はたくさんいます。色覚障害(CVD)は、特定の色の識別能力に影響を与える例です。Spectrumを使えば、さまざまなタイプの色覚障害でウェブサイトがどのように見えるかをテストできます。

Site Palette

ex10

Site Paletteを使うと、ウェブサイトからカラーパレットとして色を抽出できます。いくつかのパレットジェネレータに対応しています。共有可能なリンクを作成したり、パレットをPDF形式で印刷・保存したりできます。

Toby

ex11

Tobyはブラウザのタブを整理します。タブを一度に1000個開いておく必要はありません。新しいタブ毎に視覚的なワークスペースがあり、それをドラッグ&ドロップで追加できます。生産性を上げるのに、すごく便利です。

Talend API Tester

ex12
Talend API Testerは、ブラウザ内からREST、SOAP、HTTP
APIを操作できる素晴らしい拡張機能です。例えば、Postmanと同様です。無料版はすべての種類のHTTPリクエストを処理し、セキュリティと認証も対応しています。

Full Page Screen Capture

ex13

ページ全体のスクリーンショットを、追加のパーミッションを要求せずに撮りたいと思ったことはありませんか?Full Page Screen Captureがぴったりです。アイコンのクリックかショートカットで新しいタブに移動するだけで、結果を画像やPDFでダウンロードできます。スクロール可能な要素やiframeでも動作します!

以上が、素晴らしいブラウザ拡張機能のコレクションです。日々の開発の仕事で使ってもらえたら嬉しいです。

翻訳協力

Original Author: Simon Holdorf (@simonholdorf)
Original Article: 13 Must-Have Browser Extensions for Web Developers
Thank you for letting us share your knowledge!

この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。
選定担当: @gracen
翻訳担当: @gracen
監査担当: -
公開担当: @gracen

ご意見・ご感想をお待ちしております

今回の記事はいかがでしたか?
・こういう記事が読みたい
・こういうところが良かった
・こうした方が良いのではないか
などなど、率直なご意見を募集しております。
頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に
コメント欄にてご投稿ください。Twitterでもご意見を受け付けております。
皆様のメッセージをお待ちしております。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React: コールバックを条件によって無効にする ー useCallbackをあえてuseMemoで書き替えた例

フックuseMemouseCallbackは、コンポーネントの再描画のたびに値を再計算したり、関数インスタンスがつくられるのを避け、必要なときだけ処理するテクニックです。いわばキャッシュのような仕組みで、「メモ化」と呼ばれます。

前に書いた記事「React: コンポーネントのロジックをカスタムフックに切り出す ー カウンターの作例で」のサンプル001として、CodeSandboxに掲げたカウンターの作例を使ってご説明します。本稿で解説する修正を加えたサンプルコードも、CodeSandboxに公開しました。

メモ化は、つねに最適化につながるとはかぎりません1。とくにこのサンプルのように単純なコードではなおさらです。本稿は、フックuseMemouseCallbackの使い方についてご理解いただくことを主眼とします。

カウンターの数字の色を正負で変える

まずは、カウンターの数字がマイナスになったとき、色を赤くしてみましょう。メモ化は使わないつぎのコードで、数字の正負によってカラーが変わります(図001)。

src/CounterDisplay.js
const CounterDisplay = ({ counter }) => {
    return (
        <div>

            {/* <span>{counter.count}</span> */}
            <span style={{color: counter.count < 0 ? 'red' : 'black'}}>{counter.count}</span>

        </div>
    );
}

図001■カウンターの数字の色が正負で変わる

2101001_001.png
2101001_002.png

useMemoフックで値をメモ化する

useMemoフックには、引数がふたつあります。第1引数は関数で、戻り値がメモ化された値です。関数本体には、値を算出するための処理が書かれます。第2引数は配列で、要素は変更があったとき再計算(依存)すべき変数です。

const メモ化された値 = useMemo(算出関数, [依存配列])

第2引数の依存配列は、構文上は省けます。けれど、その場合コンポーネントがレンダーされるたびに再処理されますので、フックを使う意味がありません。適切な変数を加えてください。

前掲のカウンター表示のモジュールでuseMemoを使うには、つぎのコードのように書き替えます。モジュールの記述全体は、以下のコード001のとおりです。依存配列に加えたカウンター(counter.count)の値が変わると、再計算されます。

src/CounterDisplay.js
// import React from "react";
import React, {useMemo} from "react";

const CounterDisplay = ({ counter }) => {
    const color = useMemo(
        () => counter.count < 0 ? 'red' : 'black'
        , [counter.count]
    );
    return (
        <div>

            {/* <span style={{color: counter.count < 0 ? 'red' : 'black'}}>{counter.count}</span> */}
            <span style={{color: color}}>{counter.count}</span>

        </div>
    );
}

コード001■useMemoを使ったカウンター表示のモジュール

src/CounterDisplay.js
import React, {useMemo} from "react";

const CounterDisplay = ({ counter }) => {
    const color = useMemo(
        () => counter.count < 0 ? 'red' : 'black'
        , [counter.count]
    );
    return (
        <div>
            <button onClick={counter.decrement}>-</button>
            <span style={{color: color}}>{counter.count}</span>
            <button onClick={counter.increment}>+</button>
        </div>
    );
}
export default CounterDisplay;

useCallbackフックでコールバック関数をメモ化する

useCallbackフックは、処理する関数をメモ化します。useMemoと似た構文で、違いは第1引数が呼び出すコールバック関数だということです。第2引数には、useMemoと同じ依存配列を渡します。

const メモ化された関数 = useCallback(コールバック関数, [依存配列])

useCallbackと同じ処理を、useMemoでつぎのように書くこともできます。この構文を見やすく書けるようにしたのが、useCallbackです。次項では、あえてこの回りくどい構文を使ってみます。

const コールバック関数 = useMemo(() => コールバック関数, [依存配列])

カスタムフックのモジュールで、カウンター減算と加算のコールバック関数はuseCallbackフックでつぎのように書き直せます。依存配列に加えたカウンター(count)の値が変わったら、関数は生成し直されるということです。

src/useCounter.js
// import { useState } from 'react';
import { useCallback, useState } from 'react';

export const useCounter = (initialCount = 0) => {

    // const decrement = () => setCount(count - 1);
    const decrement = useCallback(() => setCount(count - 1), [count]);
    // const increment = () => setCount(count + 1);
    const increment = useCallback(() => setCount(count + 1), [count]);

};

ここで、状態(state)設定関数(useState())の構文について補っておきましょう。引数に値を渡すほかに、「関数型の更新」という書き方があります。引数の関数は、現在の状態値を引数に受け取り、戻り値が新たな値になるのです。

useCallbackで書き替えた前掲のコードで、関数型の更新を用いれば、依存変数はなくせます。依存なしの場合には、第2引数に空の配列[]を渡してください。すると、コンポーネントがはじめて描画されたときのみコールバック関数がつくられることになり、無駄な再生成が省けるのです。

src/useCounter.js
export const useCounter = (initialCount = 0) => {
    const [count, setCount] = useState(initialCount);
    // const decrement = useCallback(() => setCount(count - 1), [count]);
    const decrement = useCallback(() => setCount((_count) => _count - 1), []);
    // const increment = useCallback(() => setCount(count + 1), [count]);
    const increment = useCallback(() => setCount((_count) => _count + 1), []);
    return { count, decrement, increment };
};

条件によってコールバックを無効にする

ここで、カウンターの減算を-5で止めましょう。コールバックに、つぎのように条件を加えれば済むことです。

src/useCounter.js
// const decrement = useCallback(() => setCount((_count) => _count - 1), []);
const decrement = useCallback(() => {
    if (count < -4) { return; }
    setCount((_count) => _count - 1);
// }, []);
}, [count]);

でも、減算ボタンをクリックしたときの、コールバック関数の呼び出しは止まりません。コールバックを無効にしてしまえないでしょうか2。コードのイメージは、このような感じです。

src/useCounter.js
const decrement = useCallback(() =>
    (count < -4) ? null
    : setCount((_count) => _count - 1)
, [count]);

けれど、useCallbackの引数には、関数呼び出しや代入など、何らかの処理を与えなければなりません。nullという値は受け取れないのです。

Expected an assignment or function call and instead saw an expression.

useMemoの算出関数は、値が返せました。そして、関数も値に含まれます。つまり、useMemoを用いれば、つぎのように書き替えられるのです。書き直したカスタムフックのモジュールの記述は、以下のコード002にまとめました。各モジュールのコードと、実際の動きはCodeSandboxに公開したサンプルコードでお確かめください。

src/useCounter.js
// import { useCallback, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';

export const useCounter = (initialCount = 0) => {

    // const decrement = useCallback(() => {
    const decrement = useMemo(() =>
        // if (count < -4) { return; }
        (count < -4) ? null
        // setCount((_count) => _count - 1);
        : () => setCount((_count) => _count - 1)
    // }, [count]);
    , [count]);

};

コード002■useMemoでコールバック関数を条件によって無効にする

src/useCounter.js
import { useCallback, useMemo, useState } from 'react';

export const useCounter = (initialCount = 0) => {
    const [count, setCount] = useState(initialCount);
    const decrement = useMemo(() => 
        (count < -4) ? null
        : () => setCount((_count) => _count - 1)
    , [count]);
    const increment = useCallback(() => setCount((_count) => _count + 1), []);
    return { count, decrement, increment };
};

  1. useMemouseCallbackの最適化の観点からの解説としては、「雰囲気で使わない React hooks の useCallback/useMemo」や「React.memo / useCallback / useMemo の使い方、使い所を理解してパフォーマンス最適化をする」が参考になるでしょう。 

  2. 今回の例であれば、減算ボタンにdisabled属性を定めれば、onClickハンドラを無効にできます。ただ、本稿ではuseMemouseCallbackを軸に考えましょう。ご参考までに、CodeSandboxのサンプルコードには、コメントアウトしてdisabledを使った記述が添えてあります。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

いま見ているTwitterアカウントに限定してツイートを検索するブックマークレット

はじめに

ブラウザ版Twitter用ブックマークレットです。
アカウントを指定してツイートを検索する時に、コピー&ペーストする手間をひとつ省く便利なやつです。

javascript:(()=>{const t=location.href.split("/");if(t[2].endsWith("twitter.com")&&t[3]){const o=t[3].split("?")[0],i=prompt("Search from @"+o,"");i&&(location.href="//twitter.com/search?f=live&q="+i+"%20from:"+o)}})();

使い方

  1. 検索対象とするTwitterアカウントのホーム、またはツイートのページを予め開いておく。
  2. ブックマークレットを実行。
  3. 入力画面が表示されるので、キーワードを入力する。

簡潔な説明

圧縮前のコードはこちら。

(
    () => {
// ----------

const path = location.href.split('/');

// twitter ID check
if (path[2].endsWith('twitter.com') && path[3]) {
    // id
    const id = path[3].split('?')[0];// no parameters
    // keywords
    const keywords = prompt('Search from @' + id, '');

    // search results
    if (keywords) {
        location.href = '//twitter.com/search?f=live&q=' + keywords + '%20from:' + id;
    }
}

// ----------
    }
)();

大体の流れは次の通りです。

  1. URLからtwitter IDを取得する => path[3]
  2. window.promptで検索キーワードを取得する => keywords
  3. 取得した値をもとに検索パラメータをセットする
  4. 組み立てたURLを開く

  • iPhone: Safari, Chromeで動作確認しました。
  • 以下の例のように、https://twitter.com/ にアカウントが続くURLでのみ実行可能です。

OK :smiley:
https://twitter.com/Qiita
https://twitter.com/Qiita/status/1351820856100487168

NG :confounded:
https://twitter.com
https://twitter.com/search?q=qiita

あとがき

今どきwindow.promptを使うのがちょっとアレなんですが、取り敢えず動けばOKです。

いつもお世話様です。
本日もありがとうございました。

参考

【簡単にできる】ブックマークレットのすゝめ【初心者向け】
https://qiita.com/len_crow/items/189603f2c5f462bb670c

Twitterの高度な検索
1. https://twitter.com/search-advanced?lang=ja
2. https://help.twitter.com/ja/using-twitter/twitter-advanced-search

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[JavaScript] 実は使えるようになっていた、論理積代入 (&&=)と論理和代入 (||=)とNull 合体代入 (??=)

皆さんが待望していた(?)演算子が実は使えるようになっています!(ただし、IEは除く)
無くても困らないが、ちょっと助かったりするそんな演算子たち。
早速どんな機能なのかをサッと確認していきましょう。

論理積代入 (&&=)

論理積代入 (x &&= y) 演算子は、x が truthy である場合にのみ代入します。

let truthy = true;
let falsy = false;

truthy &&= false; // => false
falsy &&= true; // => false

ちなみに、truthyな値とはfalsyな値以外のことを指します。
0""(空文字)、falseNaNnullundefinedはfalsyって覚えておけば、ほぼ大丈夫です。

論理和代入 (||=)

論理和代入演算子 (x ||= y) は、x が falsy である場合にのみ代入します。

let truthy = true;
let falsy = false;

truthy ||= false; // => true
falsy ||= true; // => true

論理和演算子(||)の短略評価を利用した、値を代入するやつが若干スッキリしますね!

// val => ""
const val = val || "default" // => valに"default"が代入される
const val ||= "default" // => 上記と同じ結果

Null 合体代入 (??=)

Null 合体代入 (x ??= y) 演算子は、x が nullish (null または undefined) である場合にのみ代入を行います。

let nullish1 = null;
let nullish2 = undefined;
let truthy = true;
let falsy = false;

nullish1 ??= "nullish"; // => "nullish"
nullish2 ??= "nullish"; // => "nullish"
truthy ??= "nullish"; // => true
falsy ??= "nullish"; // => false

論理和代入のスコープが狭まった感じですね。
nullishである場合にのみ代入されるってのを上手く使える場面で使用していきましょう。

// val => null 
val == null ? "" : val; // => ""
val ??= ""; // => 上記と同じ結果

参考

https://developer.mozilla.org/ja/docs/Web/JavaScript

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

○○まで後何日?アプリを作成してみた

jsのDateオブジェクトは使いにくいのでmoment.jsというライブラリを使って作成する。

日付はドラえもんが生まれる日に設定した。

var timer //タイマー設定

const will = moment("2112-09-03 12:00")//日付選択

const doraemon = document.createElement("p")//要素作成
const body = document.querySelector('body');//親ノード取得
body.appendChild(doraemon)//子ノード追加




function count() {
    let date = moment();
    let secondLeft =(will.diff(date,"second")%60)
    let minuteLeft=(will.diff(date, "minute") % 60)
    let hourLeft=(will.diff(date, "hour") % 24)
    let dayLeft=(will.diff(date, "days") % 24)
    let monthLeft=(will.diff(date, "month") % 12)
    let yearLeft=(will.diff(date, "year"))
    doraemon.textContent = `ドラえもんが生まれるまで後${yearLeft}${monthLeft}${dayLeft}${hourLeft}${minuteLeft}${secondLeft}`
}

count()
timer = setInterval("count()", 1000)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript, Java】数値の桁数を取得する方法

プログラミング勉強日記

2021年1月25日
数値の桁数の取得方法に困ったので、備忘録として書く。

JavaScriptで数値の桁数を取得する

let number = 12345;
let numberDigit = String(nuber).length;
console.log(numberDigit);   // 5

Javaで数値の桁数を取得する

int number = 12345;
int numberDigit = String.valueOf(number).length();   
System.out.println(numberDigit);  // 5

入力した整数値の桁数を出力するプログラム

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>sample</title>

  <script type="text/javascript">
    //数値から桁数を取得する
    function getNumber() {
        // フォームから数値を取得する
        let inputNumber = document.getElementById("number").value; 
        // 取得した数値から整数部分の桁数を取得する
        let integerDigit = parseInt(inputNumber).toString().length; 
        // 桁数を出力する
        let output = "";
        if (inputNumber > 0) {
            output = integerDigit + "";
        }
    document.getElementById("digit").innerHTML = output;
    }
</script>
</head>

<body>
    <form>
        <input type="text" id="number"  onKeyUp="getNumber()">
    </form>
    <p id="digit"></p>
 </body>
</html>

実行結果
image.png

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptで最後の単語の長さ取得

説明

  • 文字列がスペースで区切られたいくつかの単語で構成されている場合、文字列の最後の単語の長さを返します。

入力: s = "Hello World"
出力: 5

ソース

JavaScript
/**
 * @param {string}
 * @return {number}
 */
var lengthOfLastWord = function(s) {
    return s.trim().split(' ').pop().length;  
};

説明

  • trim()で文字列の最初と最後のスペースをすべて削除します。
  • split(' ')文字列をスペースで分割して配列に変換。
  • pop()で最後の要素を返す。
  • .lengthで上記の要素の長さにアクセス。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptの非同期処理について①

業務でTypeScript(JavaScript)を使うようになり、なんとなくで実装していた非同期処理について学習した結果をアウトプットしたいと思い、初投稿することにしました。
文章構成等、下手くそな部分もあるかも知れませんが、温かく見守ってやってください。

そもそも非同期処理って?

非同期処理は、あるタスクが実行をしている際に、他のタスクが別の処理を実行できる方式である。同期処理では、あるタスクが実行している間、他のタスクの処理は中断される方式である。
【参考サイト】(http://ossforum.jp/node/753)

上記にも記載ある通り、非同期処理は、あるタスクが実行中に他のタスクも実行できるような実行方式です。
何の話?となるかもしれませんが、まずは非同期処理を学ぶ前に「同期処理」とはなにかから振り返ってみましょう。


・同期処理

同期処理は、プログラムを1つ1つ上から順番に実行していく方式です。

function func1(){
  return "Function1!!!";
}
function func2(){
  return "Function2!!!";
}
console.log(func1());
console.log(func2());

上記のコードですと、単純に上から順番に実行され、
1.「Function1!!!」
2.「Function2!!!」
の順番でコンソールにメッセージが出力されます。順番に処理が実行される、これが同期処理です。プログラムが上から順番に1つずつ実行され、処理の完了を待ってから次の処理が実行されていきます。
上から順番に処理が実行されていくので、例えばfunc1の処理時間が10分かかるとすると、func2が実行されるのはfunc1の処理が完了した後(10分後)となります。
同期処理は、時間のかかる処理があると、その処理が完了するまで他の処理の実行ができません。その間ユーザも操作できない、トータルの実行時間もかかってしまう・・・それならば時間のかかる処理は別の場所に投げてしまい、その間に軽い処理を行ってしまおう!という考えから作られたのが非同期処理です。


・非同期処理

非同期処理は、先ほどお伝えした通り、あるタスクが実行中に他のタスクも実行できるような処理方式です。
参考に以下コードを考えてみましょう。

console.log('First');
setTimeout(() => console.log('Second'), 2000);
console.log('third');

同期処理の考えですと、以下順番でコンソールにメッセージが表示されるはずです。
1.「First」
2. 2000ミリ秒後に「Second」
3.「third」

ですが、実際には
1.「First」
2.「third」
3. 2000ミリ秒以降に「Second」
という順番でメッセージが表示されます。なぜでしょう・・・??

・setTimeout

setTimeoutはコールバック関数と、実行遅延時間を引数に取るメソッドです。
実行遅延時間が経過したらコールバック関数の処理を開始するようなイメージです。

setTimeout('コールバック関数', '実行遅延時間');

setTimeout自身は非同期処理を行うメソッドで、処理が呼ばれた時点で別の場所に処理を投げて、時間が経過した後にコールバック関数を実行する形になっています。
このように、非同期処理は、外部に処理を投げてその間に他の処理を実行し、外部に投げた処理が完了した段階でまた処理を再実行するような仕組みとなっています。

まとめ

同期処理、非同期処理について解説いたしました。かなり簡単な内容なため、特に参考にする方はいないかもしれませんが。次は、同期処理・非同期処理がどのように実行されているか(コールスタックの説明など)を書いていけたらと思います。拙い文章ですが、ご覧いただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む