サルでもわかるWEBプログラミング

フリーソフトのみでホームページ作成

ユーザ用ツール

サイト用ツール


サイドバー

目次

サルでもわかる機械学習

sidebar

00.javascript:01.同期処理と非同期処理


01.JavaScriptの同期処理と非同期処理

JavaScriptの個人的に気になるところ

個人的に、setInterval, Promise, async, awaitという文字列が出てきた瞬間にお手上げです!コールバックとか非同期処理という言葉も見たくありません!

しかし、避けて通れなさそうなので、可能な範囲でまとめてみたいと思います。

同期処理と非同期処理とは?

同期処理はマルチタスク、非同期処理は1つずつ順番に処理、、、でもない?

2つ以上の処理を実行するとき、同期処理と非同期処理の2つの方式があります。

例えば、JavaScriptで、処理A, 処理B, 処理Cがあり、以下のように記載されているとします。

処理A
処理B
処理C

ここで、説明の都合上、

  1. 処理A : 処理開始から処理終了まで0秒かかる
  2. 処理B : 処理開始から処理終了まで0.5秒かかる
  3. 処理C : 処理開始から処理終了まで0.3秒かかる

とします。

同期処理は、

  1. 処理Aを開始 (時刻 t = 0 [sec])
  2. 処理Aが終了してから、処理Bを開始 (時刻 t = 0 [sec])
  3. 処理Bが終了してから、処理Cを開始 (時刻 t = 0.5 [sec])
  4. 処理Cが終了 (時刻 t = 0.8 [sec])

というように処理する(3つの処理すべてが終了するのに0.8秒かかる)のに対し、
非同期処理は、

  1. 処理Aを開始 (時刻 t = 0 [sec])
  2. 処理Aの終了を待たずに、処理Aの開始直後に、処理Bを開始 (時刻 t = 0 [sec])
  3. 処理Bの終了を待たずに、処理Bの開始直後に、処理Cを開始 (時刻 t = 0 [sec])
  4. 処理Aが終了 (時刻 t = 0 [sec])
  5. 処理Cが終了 (時刻 t = 0.3 [sec])
  6. 処理Bが終了 (時刻 t = 0.5 [sec])

のように処理を行う方式(3つの処理すべてが終了するのに0.5秒かかる)です。非同期処理で、何かしらの結果を表示するような複数の処理を扱う場合、とりあえず上から順番に処理を開始するが、結果が出るのを待たずにどんどん最後の処理まで開始し、結果が出た順番(処理が終了した順番)に表示するといった感じになります。

以下のサイトの、同期処理をSubway方式非同期処理をドトール方式としての説明が感動的に分かりやすいです。
非同期処理の概念について解説

JavaScriptはシングルスレッド

JavaScriptはシングルスレッドで動いているので並列処理はできない。非同期処理はできる。

詳しくはこちらのサイトが分かりやすいです。

2019年現在、時代は非同期処理!

Webページを表示するときに、非同期処理を用いると、同期処理のときと比較して、閲覧者の待ち時間が減ります!

非同期処理のデメリット

同期処理のときと比べて、開発者が書くコードが煩雑になります。

閲覧者(お客様)vs開発者(お店)、まあ、非同期処理の勉強をするしかないですね。

そこで、煩雑化するコードを少しでも見通しよく書けるように、2015年にPromiseが、2017年にAsyncとAwaitが策定されました。

setInterval関数とclearInterval関数

setInterval関数は、指定した時間ごとに処理を実行する関数です。

ちなみに、『関数f』とは、任意のxに対して、あるyをただ1つ返す『ブラックボックス』のことです。

// setInterval関数の構文
let intervalID = setInterval(func, delay[, param1, param2, ...]);

第1引数 func : ミリ秒単位の delay が経過するたびに実行する 関数(必須)。この関数funcには引数が渡されず、また戻り値を想定していない。

第2引数 delay : ミリ秒単位の数値が入る(必須

第3引数以降param1, param2, … ,paramN:タイマーが満了したときに、func で指定した関数に渡す追加の引数。

インターバルを一意に識別する intervalID を返します。

clearInterval関数によりインターバルを削除(funcの繰り返しを停止させる)できます。

例1

const log1 = function(){
  console.log("test");
};

setInterval(log1, 3000);
// 3秒ごとに"test"と表示される

処理に引数を渡したい場合は、第3引数以降に指定します。

例2

const log2 = function(msg1){
  console.log(msg1);
};

setInterval(log2, 3000, "Hello");
// 3秒ごとに"Hello"と表示される

setIntervalを用いてタイマーを設定する場合、指定された処理は指定した時間ごとに実行され続ける。
解除したい場合は、setIntervalの返り値intervalIDを定義し、clearInterval(intervalID)を実行する。

例3

let i = 0;
const log3 = function(){
  console.log("test");
  i++;
  console.log(i);
};

const intervalID = setInterval(function(){
  log3();
  // i が3より大きくなると、setInterval内の処理が止まる。
  if (i > 3) {
    clearInterval(intervalID)
  }}, 3000);

ChromeでCtrl+Shift+Iでconsole画面を開き、上記コードを入力してEnterを押すと、以下のようになります。

サンプルコードは、Javascriptのタイマー処理 setIntervalとsetTimeout(@chihiro 2018年10月30日に投稿)https://techacademy.jp/magazine/5537を参考にさせていただきました。

setInterval関数に関するリンク

https://qiita.com/chihiro/items/b105a901bbfd6c5b483c
@chihiro
2018年10月30日に投稿
Javascriptのタイマー処理 setIntervalとsetTimeout

https://techacademy.jp/magazine/5537
JavaScriptでsetIntervalを使う方法【初心者向け】
2017/5/14
→動画もあって、わかりやすいです。

https://techacademy.jp/magazine/5541
JavaScriptでsetTimeoutを使う方法【初心者向け】
2017/5/14

https://uxmilk.jp/11609
2016/2/22更新
JavaScriptでのタイマー処理の方法:setInterval()

https://www.nishishi.com/javascript-tips/setinterval-passage.html
《2017年6月13日 10:30 AM 公開/更新》
経過時間(秒数)をリアルタイムに表示する方法

https://wp-p.info/tpl_rep.php?cat=js-intermediate&fl=r6
タイマー処理を行うsetTimeout()と一定時間の繰り返しを行うsetInterval()

https://ja.javascript.info/settimeout-setinterval
29日 七月 2019
スケジューリング: setTimeout と setInterval

https://qiita.com/jkr_2255/items/fe72a451d1fc4fa81ba3
@jkr_2255
2019年03月24日に投稿
setIntervalより正確な時計を作る

https://developer.mozilla.org/ja/docs/Web/API/Window/setInterval
setInterval() - MDN - Mozilla

余談ですが、setInterval関数の中で“this”を用いるときは、注意が必要です。
参考:setInterval()の外でlet self = thisとしてthisオブジェクトを別の変数に割り当てる(JavaScriptの”this”)

Promise

Promise は非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。(引用元

『オブジェクト』とは関連のあるデータand/or機能の集合です。(引用元)
(たいていは変数と関数で構成されており、オブジェクトの中ではそれぞれプロパティとメソッドと呼ばれます。)

??

とりあえず、『objectオブジェクト』は、『property(データ)』と『method(機能)』を持っているものみたいです。

// Promiseの構文
new Promise(executor);

executorは、2つの引数 resolve と reject を取る関数。executor 関数は、

  1. resolve 関数と
  2. reject 関数

が渡されて Promise が実装されるとすぐに実行されます (Promise コンストラクターがオブジェクトを返すよりも前に executor は実行されます)

上記の構文のexecutorの部分を『アロー関数』を用いて表すと、以下のようになります。

// Promiseの構文
new Promise((resolve, reject) => {statements})
  • resolve : 処理の成功を通知するための関数
  • reject : 処理の失敗を通知するための関数
  • statements : 処理本体

参考:改訂新版JavaScript本格入門

// Promiseのthenメソッドの構文
promise.then(success, failure)
  • promise : Promiseオブジェクト
  • success : 成功コールバック関数(resolve関数によって呼び出し)
  • failure : 失敗コールバック関数(reject関数によって呼び出し)

then関数の引数success/failureは、それぞれresolve/reject関数で指定された引数を受け取って、成功/失敗時の処理を実行します。

Promiseは複数の非同期処理を連結するときに便利

Promise インターフェイスは、作成時点では分からなくてもよい値へのプロキシです。(?)
非同期メソッドは、最終的な値を返すのではなく、未来のある時点で値を持つ Promise を返すことで、同期メソッドと同じように値を返すことができるようになり、複数の非同期メソッドを連結するときにもコードが比較的きれいに書けるようになります。

Promise の状態は以下のいずれかとなります。

  1. pending: 初期状態。成功も失敗もしていません。
  2. fulfilled: 処理が成功して完了したことを意味します。
  3. rejected: 処理が失敗したことを意味します。

引用元:Promise - JavaScript MDN

長々と定義?っぽいことを書いてきましたが、やっと、例をいくつか記載して、Promiseを実際に使えるようにしたいと思います。

なんとなく用語集

  • クラス:設計図(or 分類)
  • インスタンス:設計図をもとに作ったモノ
    • クラスをnewしてインスタンスを作る)
    • JavaScriptの場合は、プロトタイプをnewしてインスタンスを作る
  • オブジェクト:モノ(プロパティとメソッドをもつ、モノ)
  • コンストラクタ:クラスをnewしたときに実行される関数

Promiseの使用例1

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise

var promise1 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve('foo');
  }, 300);
});
 
// 『処理1』
promise1.then(function(value) {
  console.log(value);
  // expected output: "foo"
});
 
// 『処理2』
console.log(promise1);
// expected output: [object Promise]

[object Promise]が最初に表示され、その約0.3秒後に“foo”が表示されます。

上記のコードを上から順番に読んでいくと、以下のようになります。

  1. Promiseプロトタイプ(クラスのようなもの)から、promise1オブジェクトを作成
  2. Promiseの中のsetTimeout()関数の処理が成功したら、'foo'という値を返す
  3. promise1オブジェクトの処理が終了したら(then)、promise1オブジェクトが返した値(value)に対して、consoleに(value)を表示する。(今回は、0.3秒待った後、'foo'という値を返すので、console画面にfoo と表示する)
  4. consoleにpromise1オブジェクトを表示する

実際は、以下のようになります。

  1. 『処理1』を開始 (時刻 t = 0 [sec])
  2. 『処理1』開始のほぼ0秒後に『処理2』を開始する (時刻 t = 0 [sec])
  3. 『処理2』開始のほぼ0秒後に、『処理2』が終了し、[object Promise] がconsoleに表示される (時刻 t = 0 [sec])
  4. 『処理1』開始の0.3秒後に、『処理1』が終了し、'foo'がconsoleに表示される (時刻 t = 0.300 [sec])

Promiseの使用例2

引用元:改訂新版JavaScript本格入門
7.5.1 Promiseオブジェクトの基本を押さえる

function asyncProcess(value) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 引数valueが未定義であるかどうかによって成否を判定
      if (value) {
        resolve(`入力値: ${value}`);
      } else {
        reject('入力は空です');
      }
    }, 500);
  });
}
 
asyncProcess('アンパンマン').then(
  // 成功したときに実行される処理
  response => {
    console.log(response);
  },
  // 失敗したときに実行される処理
  error => {
    console.log(`エラー: ${error}`);
  }
);
// expected output: 入力値: アンパンマン

上から順番に読んでいくと、以下のようになります。

  1. 非同期処理をasyncProcess関数としてまとめる(asyncProcess関数は、Promiseオブジェクトを返す。)
  2. Promiseの中の非同期処理を、アロー関数として記述
    1. 引数valueが存在するときは、consoleに入力値: (valueの内容)と表示。
    2. 引数valueが存在しないときは、consoleに入力は空です%%と表示。
  3. asyncProcess('アンパンマン')を実行する

Promiseの使用例3

引用元:JavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみる

// 150円のりんごを1つ買うpromiseBuyApple関数
// 第一引数に支払い金額
// 成功したらおつりを計算して返す
// お金が足りなければエラー文章を返す
var promiseBuyApple = function(payment){
  return new Promise(function(resolve, reject){
    if(payment >= 150){
      resolve(payment-150);
    }else{
      reject('金額が足りません。');
    }
  });
}
 
//りんごをひとつ買う
promiseBuyApple(400)
.then(change=>console.log('おつりは' + change + '円です'))
.catch(error=>console.log('エラーが発生しました:' + error));
 
////りんごをたくさん買う
promiseBuyApple(400).then(change=>{
  console.log('おつりは' + change + '円です');
  return promiseBuyApple(change);
}).then(change=>{
  console.log('おつりは' + change + '円です');
  return promiseBuyApple(change);
}).then(change=>{
  console.log('おつりは' + change + '円です');
}).catch(error=>{
  console.log('エラーが発生しました:' + error);
});

Promiseに関するリンク

https://qiita.com/YoshikiNakamura/items/732ded26c85a7f771a27
@YoshikiNakamura
2018年10月25日に更新
JavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみる
→特に最初の方の、JavaScriptにおける同期処理、非同期処理の説明は必見です。また、後半で具体的なコードの記載と分かりやすい解説があり、繰り返し読むことにより、なんとなくPromiseを理解できそうな気になれます。

https://azu.github.io/promises-book/#chapter1-what-is-promise
JavaScript Promiseの本
azu
→わかりやすいです。必見です。繰り返し読んで写経すると非同期処理が怖くなくなりそうです。

https://rightcode.co.jp/blog/information-technology/javascript-promise
【JavaScript】Promise で非同期処理を記述する
ライトコードメディア編集部 ライトコードメディア編集部
2019.05.29

https://www.sejuku.net/blog/52314
【JavaScript入門】誰でも分かるPromiseの使い方とサンプル例まとめ!
マサト マサト
2019/2/10

https://qiita.com/valley/items/4cf782bd04170fddd5d5
@valley
2018年07月07日に更新
僕なりのPromise入門

https://qiita.com/tfrcm/items/1dfe4265c36bea903ab3
@tfrcm
2017年05月16日に更新
Promise再入門① ~Promise基本編~

https://wa3.i-3-i.info/word13646.html
コンストラクタ (constructor)
→すごく分かりやすいです。必見です。

(引用ここから)


  • クラス:設計図
  • インスタンス:実際に作った物
  • オブジェクト:モノ(クラスとかインスタンスとかをふんわりと表現したもの)
  • コンストラクタ:「クラスをnewしたときに実行される関数」

(引用ここまで)

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
Promise - JavaScript | MDN - Mozilla
→この項は、わりと分かりやすいです。

https://techracho.bpsinc.jp/hachi8833/2017_11_16/48168
2017年11月16日
JavaScript: 5分でわかるPromiseの基礎(翻訳)

https://qiita.com/toshihirock/items/e49b66f8685a8510bd76
@toshihirock
2016年04月17日に更新
Promiseについて0から勉強してみた
Promise.allのところ、

taskAとtaskBは並行で実行されている(直列で実行されている場合には差分の秒数として26が表示されるはず)

のところは、

  1. taskA実行開始、スレッドはtaskAの結果を待たずにすぐに次へ(時刻t=0)
  2. taskB実行開始(時刻t=0)
  3. taskBの結果が返ってくるので受けとる(時刻t=10)
  4. taskAの結果が返ってくるので受けとる(時刻t=16)

ということなのだと思います。

https://qiita.com/koki_cheese/items/c559da338a3d307c9d88
@koki_cheese
2014年12月05日に更新
JavaScript Advent Calendar 20145日目
今更だけどPromise入門

https://azu.github.io/promises-book/
JavaScript Promiseの本
azu

https://qiita.com/saka212/items/9b6cfe06b464580c2ee6
@saka212
2020年05月05日に更新
Promiseとthenのメソッドチェーン(直列・並列・値の受け取り・引数)

https://sbfl.net/blog/2019/02/08/javascript-callback-func/
JavaScriptの「コールバック関数」とは一体なんなのか
2019/02/08

asyncとawait

async/awaitを利用すると、Promiseを利用した構文よりも、簡潔に非同期処理を書くことができます。

async function 宣言は、 AsyncFunction オブジェクトを返す 非同期関数 を定義します。

async/awaitの構文

async function name([param[, param[, ... param]]]) {
   statements
}
  1. name : 関数名
  2. param : 関数に渡す引数名
  3. statements : 関数の本体を構成するステートメント

このようにasyncを記述しておくと、この関数function name() { }はPromiseを返すようになります。また、「await」はこの関数のstatements の中に

async function name([param[, param[, ... param]]]) {
  await Promise処理1   
  処理2
}

のように記載します。このように記載すると、『Promise処理1』が終了するまで、『処理2』が開始されません。
await」はPromise処理の結果が返ってくるまで、処理を一時停止してくれる演算子となります。

引用元:https://www.sejuku.net/blog/69618

await 式は async function の実行を一時停止し、Promise の解決または拒否を待ちます。解決した後に async function の実行を再開します。再開するときに await 式は解決された Promise にラップされた値を返します。

Promise が拒絶された場合、await 式は理由となった値をスローします。

引用元:
async function - MDN - Mozilla
await - MDN - Mozilla

もう一度asyncの定義

async とは非同期関数を定義する関数定義。
Async Function は非同期処理を扱う(promise を利用する)関数を定義する構文。
Async Function は通常の関数とは異なり、async を関数の前に付けることで、その関数は Promise オブジェクト(Promiseインスタンス)を返すようになる。

async function asyncFunc(/*引数*/) {
  // 処理
  return value;
}

上記で定義されたasyncFuncは、以下で定義されたasyncFuncと全く同じ関数。

function asyncFunc(/*引数*/) {
  return new Promise((resolve, reject) => {
    // 処理
    resolve(value);
  });
}

引用元:https://rightcode.co.jp/blog/information-technology/javascript-async-await
https://www.webdesignleaves.com/pr/jquery/javaascript_03.html

async/awaitの使用例1

引用元:https://www.sejuku.net/blog/69618

function myPromise(num) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(num * num)
    }, 3000)
  })
}
 
async function myAsync() {
    const result = await myPromise(10);
    console.log(result);
}
 
myAsync();

async/awaitの使用例2

引用元:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/await

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}
 
async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}
f1();

async/awaitの使用例3

引用元:async/await 入門(JavaScript)@soarflat 2019年09月02日に更新

function sampleResolve(value) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(value * 2);
        }, 2000);
    })
}
 
async function sample() {
    const result = await sampleResolve(5);
    return result + 5;
}
/**
 * async関数の中で、sampleResolve()をawaitしているため、
 * sampleResolve()からPromiseの結果が返されるまで処理が一時停止される
 * 今回の場合、2秒後にresolve(10)が返ってきて
 * その後の処理(return result + 5;)が再開される
 * resultにはresolveされた10が格納されているため、result + 5 = 15がreturnされる
 */
 
sample().then(result => {
    console.log(result); // => 15
});

async,awaitに関するリンク

https://www.sejuku.net/blog/69618
【JavaScript入門】5分で理解!async / awaitの使い方と非同期処理の書き方
マサト
2019/6/11

https://qiita.com/soarflat/items/1a9613e023200bbebcb3
@soarflat
2019年09月02日に更新
async/await 入門(JavaScript)

https://qiita.com/suin/items/97041d3e0691c12f4974
@suin
2019年09月04日に更新
Promiseの使い方、それに代わるasync/awaitの使い方

https://sbfl.net/blog/2016/07/13/simplifying-async-code-with-promise-and-async-await/
Promiseとasync/awaitでJavaScriptの非同期処理をシンプルに記述する
2016年7月14日

https://webbibouroku.com/Blog/Article/js-async-await
[Javascript] async, await を使った非同期処理入門まとめ
2018.03.26

https://qiita.com/shisama/items/61cdcc09dc69fd8d3127
@shisama
2019年02月11日に更新
axios、async/awaitを使ったHTTPリクエスト(Web APIを実行)

https://qiita.com/michael-ilcsy/items/75a9c9f9e358f3582308
@michael-ilcsy
2019年03月15日に更新
async, await入門

https://qiita.com/agajo/items/73ec7651747956439f7e
@agajo
2019年01月25日に更新
Promise・Observable・async/await のそれぞれで同じ事をやってみて書き方の違いを理解しよう

https://qiita.com/valley/items/dd37deb77f06eb6c451c
@valley
2019年06月03日に更新
Promiseが分かれば簡単!async, await

https://qiita.com/SotaSuzuki/items/f23199e864cba47455ce
@SotaSuzuki
2018年05月12日に更新
Promise と async/await の理解度をもう1段階上げる

https://qiita.com/kerupani129/items/2619316d6ba0ccd7be6a
@kerupani129
2019年03月26日に更新
Promise, async, await がやっていること (Promise と async は書き換え可能?)

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function
async function - JavaScript - MDN - Mozilla

https://tech-blog.s-yoshiki.com/entry/140
async awaitで画像を読み込み canvasに描画 JavaScript
2019-06-23

https://dev.classmethod.jp/articles/explain-promise-and-async-await-for-primary-javascript/
Sの初心者にPromiseとasync/awaitの使い方を雑に説明してみる
夏目祐樹
2020.09.09

https://qiita.com/uhyo/items/0e2e9eaa30ec2ff05260
@uhyo
2020年07月30日に更新
top-level awaitがどのようにES Modulesに影響するのか完全に理解する

fetch

https://kde.hateblo.jp/entry/2018/10/22/010811
【JavaScript基礎】Fetch APIの基礎
20181022

fetch(…) の結果は、 Promiseオブジェクトして返ってくる

https://qiita.com/sotasato/items/31be24d6776f3232c0c0
@sotasato
2020年11月04日に更新
JavaScriptのFetch API について

onload

https://github.com/tensorflow/tfjs/blob/master/tfjs-converter/demo/mobilenet/index.js

const cat = document.getElementById('cat');
cat.onload = async () => {
  const resultElement = document.getElementById('result');

......

async/await

TensorFlow.js

参考サイト

https://h5y1m141.github.io/step-up-javascript/doc/understanding_javascript/async_02.html
非同期処理の概念について解説
同期処理をSubway方式非同期処理をドトール方式としての説明が感動的に分かりやすいです。

https://naito-coding0322.hatenablog.com/entry/2019/01/10/172809
2019-01-10
並列処理/並行処理とか同期処理/非同期処理について
→上記サイトのドトール方式に、さらに、専門用語の解説を加えており、必見です。

https://rightcode.co.jp/blog/information-technology/javascript-promise
【JavaScript】Promise で非同期処理を記述する
ライトコードメディア編集部 ライトコードメディア編集部
2019.05.29

https://rightcode.co.jp/blog/information-technology/javascript-async-await
【JavaScript】 async/await で非同期処理をわかりやすく記述する
ライトコードメディア編集部 ライトコードメディア編集部
2019.05.31

https://zenn.dev/qnighy/articles/345aa9cae02d9d
JavaScriptの非同期処理をじっくり理解する (1) 実行モデルとタスクキュー
2021.09.25に公開

リンク


00.javascript/01.同期処理と非同期処理.txt · 最終更新: 2021/10/07 by adash333