Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/calibreapp-image-actions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Compress Images
on:
pull_request:
# Run Image Actions when JPG, JPEG, PNG or WebP files are added or changed.
# See https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#onpushpull_requestpaths for reference.
paths:
- '**.jpg'
- '**.jpeg'
- '**.png'
- '**.webp'
jobs:
build:
# Only run on Pull Requests within the same repository, and not from forks.
if: github.event.pull_request.head.repo.full_name == github.repository
name: calibreapp/image-actions
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3

- name: Compress Images
uses: calibreapp/image-actions@main
with:
# The `GITHUB_TOKEN` is automatically generated by GitHub and scoped only to the repository that is currently running the action. By default, the action can’t update Pull Requests initiated from forked repositories.
# See https://docs.github.com/en/actions/reference/authentication-in-a-workflow and https://help.github.com/en/articles/virtual-environments-for-github-actions#token-permissions
githubToken: ${{ secrets.GITHUB_TOKEN }}
24 changes: 24 additions & 0 deletions .github/workflows/calibreapp-image-manual.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Compress Images
on:
workflow_dispatch:
jobs:
build:
name: calibreapp/image-actions
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Compress Images
id: calibre
uses: calibreapp/image-actions@main
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
compressOnly: true
- name: Create New Pull Request If Needed
if: steps.calibre.outputs.markdown != ''
uses: peter-evans/create-pull-request@v3
with:
title: Compressed Images Nightly
branch-suffix: timestamp
commit-message: Compressed Images
body: ${{ steps.calibre.outputs.markdown }}
Binary file modified meetings/2016-01-29/azu/how-to-learn-ruby.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
87 changes: 48 additions & 39 deletions source/basic/async/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@ description: "JavaScriptにおける非同期処理についてを紹介しま
同期処理ではひとつの処理が終わるまで、次の処理へ進むことができないためです。

次のコードの`blockTime`関数は指定した`timeout`ミリ秒だけ無限ループを実行し、同期的にブロックする処理です。
この`blockTime`関数を呼び出すと、指定時間が経過するまで次の処理(次の行)は呼ばれません。
この`blockTime`関数を呼び出すと、指定時間が経過するまで次の処理(タスクB)は呼ばれません。

{{book.console}}
```js
function taskA() {
console.log("タスクAを実行 at " + Date.now());
}
function taskB() {
console.log("タスクBを実行 at " + Date.now());
}
// 指定した`timeout`ミリ秒経過するまで同期的にブロックする関数
function blockTime(timeout) {
const startTime = Date.now();
Expand All @@ -39,16 +45,18 @@ function blockTime(timeout) {
}
}
}
console.log("処理を開始");
taskA();
blockTime(1000); // 他の処理を1000ミリ秒(1秒間)ブロックする
console.log("この行が呼ばれるまで処理が1秒間ブロックされる");
taskB();
```

同期的にブロックする処理があると、ブラウザでは大きな問題となります。
なぜなら、JavaScriptは基本的にブラウザのメインスレッド(UIスレッドとも呼ばれる)で実行されるためです。
メインスレッドは表示の更新といったUIに関する処理も行っています。
そのため、メインスレッドが他の処理で専有されると、表示が更新されなくなりフリーズしたようになります。

![single-thread-tasks](./img/single-thread-tasks.png)

先ほどの例では1秒間も処理をブロックしているため、1秒間スクロールなどの操作が効かないといった悪影響がでます。

## 非同期処理 {#async-processing}
Expand All @@ -64,49 +72,36 @@ JavaScriptにおいて非同期処理の代表的な関数として`setTimeout`
setTimeout(コールバック関数, delay);
```

次のコードでは`setTimeout`関数を使って10ミリ秒後に、1秒間ブロックする処理を実行しています
`setTimeout`関数でタイマーに登録したコールバック関数は非同期的なタイミングで呼ばれます
そのため`setTimeout`関数の次の行に書かれている同期的処理は、非同期処理よりも先に実行されます
次のコードでは、見た目上はタスクA → 非同期のタスク → タスクBという流れになっています
しかし、`setTimeout`関数を使い、非同期のタスクは1000ミリ秒(1秒)後に実行するようにタイマーへ登録しています
そのため、実際にはタスクA → タスクB → 非同期のタスクという順番で実行されます

{{book.console}}
```js
// 指定した`timeout`ミリ秒経過するまで同期的にブロックする関数
function blockTime(timeout) {
const startTime = Date.now();
while (true) {
const diffTime = Date.now() - startTime;
if (diffTime >= timeout) {
return; // 指定時間経過したら関数の実行を終了
}
}
function taskA() {
console.log("タスクAを実行 at " + Date.now());
}
function taskB() {
console.log("タスクBを実行 at " + Date.now());
}
function taskAsync() {
console.log("非同期のタスクを実行 at " + Date.now());
}

console.log("1. setTimeoutのコールバック関数を10ミリ秒後に実行します");
taskA();
setTimeout(() => {
console.log("3. ブロックする処理を開始します");
blockTime(1000); // 他の処理を1秒間ブロックする
console.log("4. ブロックする処理が完了しました");
}, 10);
// ブロックする処理は非同期なタイミングで呼び出されるので、次の行が先に実行される
console.log("2. 同期的な処理を実行します");
taskAsync();
}, 1000);
taskB();
```

このコードを実行した結果のコンソールログは次のようになります。

<!-- textlint-disable preset-ja-technical-writing/ja-no-redundant-expression -->

1. setTimeoutのコールバック関数を10ミリ秒後に実行します
2. 同期的な処理を実行します
3. ブロックする処理を開始します
4. ブロックする処理が完了しました

<!-- textlint-enable preset-ja-technical-writing/ja-no-redundant-expression -->

このように、非同期処理(`setTimeout`のコールバック関数)は、コードの見た目上の並びとは異なる順番で実行されることがわかります。

![非同期処理とタイマー](./img/async-single-thread-tasks.png)

## 非同期処理はメインスレッドで実行される {#async-and-main-thread}

JavaScriptにおいて多くの非同期処理はメインスレッドで実行されます
JavaScriptでは多くの非同期処理もメインスレッドで実行されます
メインスレッドはUIスレッドとも呼ばれ、重たいJavaScriptの処理はメインスレッドで実行する他の処理(画面の更新など)をブロックする問題について紹介しました(ECMAScriptの仕様として規定されているわけではないため、すべてがメインスレッドで実行されているわけではありません)。

非同期処理は名前から考えるとメインスレッド以外で実行されるように見えますが、
Expand All @@ -120,10 +115,20 @@ JavaScriptにおいて多くの非同期処理はメインスレッドで実行
この非同期処理はメインスレッドで同期的にブロックする処理の影響を受けないはずです。
しかし、実際にはこの非同期処理もメインスレッドで実行された同期的にブロックする処理の影響を受けます。

次のコードを実行すると`setTimeout`関数で登録したコールバック関数は、タイマーに登録した時間(10ミリ秒後)よりも大きく遅れて呼び出されます。
次のコードを実行すると`setTimeout`関数で10ミリ秒後に非同期のタスクを実行するように登録しています。
しかし、実際には同期的なブロックする処理により、非同期のタスクはタイマーに登録した時間(10ミリ秒後)よりも大きく遅れて呼び出されます。

{{book.console}}
```js
function taskA() {
console.log("タスクAを実行 at " + Date.now());
}
function taskB() {
console.log("タスクBを実行 at " + Date.now());
}
function taskAsync() {
console.log("非同期のタスクを実行 at " + Date.now());
}
// 指定した`timeout`ミリ秒経過するまで同期的にブロックする関数
function blockTime(timeout) {
const startTime = Date.now();
Expand All @@ -136,22 +141,26 @@ function blockTime(timeout) {
}

const startTime = Date.now();
taskA();
// 10ミリ秒後にコールバック関数を呼び出すようにタイマーに登録する
setTimeout(() => {
const endTime = Date.now();
taskAsync();
console.log(`非同期処理のコールバックが呼ばれるまで${endTime - startTime}ミリ秒かかりました`);
}, 10);
console.log("ブロックする処理を開始します");
blockTime(1000); // 1秒間処理をブロックする
console.log("ブロックする処理が完了しました");
taskB();
```

多くの環境では、このときの非同期処理のコールバックが呼ばれるまでは1000ミリ秒以上かかります。
多くの環境では、このときの非同期処理のコールバックが呼ばれるまでは1秒以上かかります。
これは、10ミリ秒後に非同期のタスクを実行するようにタイマーへ登録自体はできていますが、同期的なブロックする処理によって非同期のタスクの実行も後ろにずれてしまうためです。
このように**非同期処理**も**同期処理**の影響を受けることから、同じスレッドで実行されていることがわかります。

![非同期処理とブロックする同期処理](./img/block--async-single-thread-tasks.png)

JavaScriptでは一部の例外を除き非同期処理が**並行処理(concurrent)**として扱われます。
並行処理とは、処理を一定の単位ごとに分けて処理を切り替えながら実行することです。
そのため非同期処理の実行中にとても重たい処理があると、非同期処理の切り替えが遅れるという現象を引き起こします
そのため非同期処理の実行前にとても重たい処理があると、非同期処理の実行が遅れるという現象を引き起こします

このようにJavaScriptの非同期処理も基本的には1つのメインスレッドで処理されています。
これは`setTimeout`関数のコールバック関数から外側のスコープのデータへのアクセス方法に制限がないことからもわかります。
Expand Down
Loading