金曜日, 12月 19, 2025
No menu items!
ホーム ブログ ページ 5633

SpiritFarer Devは、Fate’s Endでゴージャスな新しい2Dアクションアドベンチャーゲームで戻ってきました



泣くだけのゲームがいくつかあります。ペスト物語の終わり:たとえば、レクイエム、またはクレアの不明瞭さの第1幕のクライマックス:遠征33-ビデオゲームがあなたを泣かせることができるなら、それは正しいことをしています。物語主導のRPGファンとして、私のSteamライブラリのSobにふさわしいゲームのリストはかなり広範囲ですが、Spiritfarerは彼らの中で最高のものであり、心を砕き、どこでも日々を台無しにしています。今、サンダーロータスチームが戻ってきました 運命の終わりに、敵があなたの兄弟であり、あなたの行動が壊れた家族を再会するか、さらに裂け目をさらに深めることができる、同様に感情的に充電されたアクションアドベンチャーゲーム。
ストーリーの残りの部分を読んでください…


続きを見る


🧠 編集部の感想:
SpiritFarerの開発チームが新作「運命の終わり」を発表したことにワクワクしています。感情豊かな物語が展開され、兄弟との関係が深く掘り下げられる点がとても魅力的です。心を揺さぶるアクションアドベンチャーゲームとしての期待が高まります。

Views: 0

【超有料級】非同期処理がスッと理解できる魔法のレッスン。今日からあなたも非同期の魔法使い! #JavaScript – Qiita



【超有料級】非同期処理がスッと理解できる魔法のレッスン。今日からあなたも非同期の魔法使い! #JavaScript - Qiita

はじめに

今回の記事では、多くの人が一度はつまずいたり、挫折した経験のある「非同期処理」について解説します。

「非同期処理」と聞いて、「うわ、無理…」「なんか苦手…」と感じる人も少なくないのではないでしょうか?
実は、僕自身もこの分野がめちゃくちゃ苦手でした。
プログラミングの処理は基本的に、上から下へ順番に実行されていきます。しかし、非同期処理では処理の途中で他の処理を実行し、非同期のタスクが完了したタイミングでその結果を使って画面を描画したり、データベースにアクセスしたりします。
たとえば画像表示サイトでは、ユーザー情報の取得を待たずに、先に画面操作ができるようになることがあります。
非同期処理は、まさにそういった仕組みを実現するために使われています。

でも正直に言うと、初めて非同期処理を学んだときは、本当に意味がわかりませんでした。
頭の中をミキサーでぐちゃぐちゃにされたような感覚で、「なんでこうなるの?」と何度も混乱しました。
それでも、何度もつまずきながら試行錯誤を繰り返し、ようやく非同期処理の考え方と仕組みが理解できるようになりました。

この記事では、そんな僕がついに「腹落ちした」非同期処理の本質について、できるだけわかりやすくまとめました。

また、この記事には簡単な練習問題も用意していますので、
読むにあたって、以下の2つの準備をおすすめします:

  • 紙とペン、そして VS Code や Cursor などのエディターを用意しましょう
     実際にコードを書きながら、一つひとつの動きを確認して進めてみてください。
  • 紙に図を書いてみましょう
     後ほど紹介する図を、自分の手で書いてみましょう。非同期処理のタスクがどこで管理され、どの順番で実行されるのかを可視化することで、理解度が一気に深まります。

この2つを実行すれば、記事を読み終えるころには、非同期処理への苦手意識がなくなっているはずです。
「騙されたと思って」ぜひ実践してみてください!

この記事を読むことで得られること

実際に手を動かしてコードを書いたり、処理順番を図にしたりすることで理解度倍増!
JavaScriptの難関、非同期処理について処理を視覚的に確認することができ、
非同期処理への苦手意識がなくなります。

目次

同期処理について

非同期処理について

  • メインスレッドから処理を一時的に切り離されて実行される

    • メインスレッドはコールスタックで管理され、メインスレッドから切り離された非同期処理はタスクキューで管理されている
    • 非同期処理ではメインスレッドから処理が切り離されているのでメインスレッドでの処理は継続される
  • タスクキュー(マクロタスク・マイクロタスク)で処理する順番を管理している(先入れ先出し)

    • コールスタックとイベントループと連携している
      • イベントループは定期的にコールスタックにコンテキストが積まれていないかを確認する仕組み
      • コールスタックに処理が積まれていない場合にタスクキューからコールスタックに処理を移行し実行する
    function sleep(ms) {
      const startTime = new Date();
      while (new Date() - startTime  ms);
      console.log('sleep done');
    }
    
    const btn = document.querySelector('button');
    btn.addEventListener('click', function(){
      console.log('button clicked');
    });
    
    setTimeout(function (){
    	sleep(3000)
    ,2000})
    
    • setTimeoutは非同期処理でメインスレッドから切り離されている。関数実行(ページ読み込み)から2000(2秒間)経って sleep(3000)が実行される
    • メインスレッドから切り離されている時間(2秒間)はメインスレッドでの後続の処理が可能

代表的な非同期処理

非同期処理API

  • setTimeout
  • Promise
  • queueMicrotask

etc…

UIイベント

etc…

NWイベント

  • HTTPリクエストの送受信
  • ソケット通信(WebSocketなど)

…etc

I/Oイベント

  • ファイル読み書き(fs.readFile など)
  • データベースとの通信
  • 標準入力や出力(ターミナルとのやりとり)

…etc

コールスタック、イベントループ、タスクキュー関係図

いろいろと用語が出てきて、「結局どう関係してるの?」と混乱しているかもしれません。
コールスタック?イベントループ?タスクキュー?
言葉だけだと関係性のイメージがつかみにくいですよね。

そんなときは、ぜひ以下の図を紙に手で書いてみてください。
Image from Gyazo

最初は難しく感じるかもしれませんが、図に描くことで処理の流れが目に見えるようになり、理解が一気に深まります。

※この段階では「ジョブキュー」と「タスクキュー」の違いは気にしなくてOKです。
どちらもまとめて「タスクキュー」として考えて進めていきましょう。

非同期処理コールバック

function a() {
  setTimeout(function task1() { 
    console.log('task1 done');
  });

  console.log('fn a done');
}

function b() {
  console.log('fn b done');
}

a();

b();

//コンソール出力
'fn a done'
'fn b done'
'task1 done'

練習問題

先程ののコードでは、a()を実行したあとにb()を実行していますが、
実行結果としては b() が task1 より先に表示されてしまいます。
では、「task1が完了したあとに関数bを実行する」には、どのようにコードを書き換えれば良いでしょうか?
先ほど紹介した関係図に書き込むと視覚的に理解できますよ!

回答

function a() {
  setTimeout(function task1() { 
    console.log('task1 done');
    b();
  });

  console.log('fn a done');
}

function b() {
  console.log('fn b done');
}

a();

コードの流れ

関数aを実行(コールスタックに関数aの関数コンテキストとグローバルコンテキストが積まれる)

→ タスクキューに関数をtask1を追加
→ コールスタックから関数aの関数コンテキストが消滅 
→ コールスタックからグローバルコンテキストが消滅 
→ タスクキューから関数task1がコールスタックに移行 
→ 関数task1が実行 
→ 関数 bを実行(コールスタックに関数bの関数コンテキストが積まれる) 
→ 関数bが実行
上記のように非同期とコールバックの仕組みを使うことで関数の順序を変えることができる

コールバック地獄

function sleep(callback, val) {
    setTimeout(function () {
        console.log(val++)
        callback(val)
    }, 1000)
}

sleep(function (val) {
    sleep(function (val) {
        sleep(function (val) {
            sleep(function (val) {

            }, val)
        }, val)
    }, val)
}, 0)

コードの流れ

sleep

  • callbackvalを引数に取る
  • setTimeout を使い1秒後、コンソールにvalを表示して表示した後にプラス1される
  • プラス1されて値がcallbackの引数に渡され処理される

sleep使用側

  • sleep の1回目は、引数 0 を受け取り、1秒後に console.log(val++)0 を表示。その後、val1 にインクリメントされ、次の callback1 が渡される。この時点では、渡された 1 はまだコンソールには表示されない(次の sleep 内で表示される)。
  • sleep の2回目は、前のコールバックから受け取った val = 1 を引数にして呼び出され、1秒後に console.log(val++)1 を表示。その後、val2 になり、次の callback2 が渡される。この時点でも、2 はまだ表示されない。

以下、繰り返し

出力結果

時間経過 val console.log(val++) の出力 次に渡すval
0秒 sleep(0) 開始
1秒後 0 0 1
2秒後 1 1 2
3秒後 2 2 3
4秒後 3 3 4

このコードは正常に動作しますが、コールバック関数のネストが深くなり、コードの可読性や保守性が著しく低下してしまいます。
このようにコールバックを連ねて非同期処理をつなげる方法は「コールバック地獄」と呼ばれ、推奨されません。

そこで、JavaScriptのPromiseやasync/awaitを使うことで、よりシンプルで読みやすい非同期処理の記述が可能になります。
次のセクションでは、同じ処理をPromiseとasync/awaitで書き直してみましょう。

Promise

  • 非同期処理をより簡単に、可読性が上がるように書けるようにしたもの

コールバック地獄の解消

function sleep(val) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      console.log(val++);
      resolve(val);
    }, 1000);
  })
};

sleep(0)
.then(val => sleep(val))
.then(val => sleep(val))
.then(val => sleep(val));

コードの流れ

sleep関数

  • 引数に val を受け取る
  • new Promise により Promise インスタンスを生成

    • Promise コンストラクタのコールバック関数は、resolvereject という2つの引数を取る(今回は resolve のみ使用)
  • setTimeout を使って1秒後に以下の処理を行う:

    • console.log(val++) で現在の val を表示し、その後 val をインクリメント
    • resolve(val) を呼び出し、次の .then() に値を渡す
  • Promise インスタンスを返すことで、.then() で非同期処理をつなげることができる

sleep使用側

  • sleep(0) を最初に呼び出し、.then() を使ってその後の処理をチェーンさせていく
  • .then() の中では次の sleep(val) を呼び出して return することで、処理の流れが順番に続いていく
  • .then() に渡された関数は、前の resolve(val) の結果を引数として受け取る
  • .then() に処理をつなげるには、常に Promise を返す必要がある。

    • resolve(val).then(val => ...)
    • reject(err).catch(err => ...) に値が渡される

.then/.catch で処理をつなげるために、必ず戻り値にPromiseのインスタンスを返す

  • resolvethen
  • reject → catch

async/await

Promiseをより直感的に書けるようにしたもの

// async 関数を変数に代入するパターン
const fetchData = async (url) => {
  const response = await fetch(url);
  // 後続の処理を書く
};

// 通常の関数宣言パターン
async function fetchData(url) {
  const response = await fetch(url);
  // 後続の処理を書く
}

コードの流れ

  • async

    関数の前に async を付けることで、その関数を非同期関数(Async Function)として定義できる。

    • async 関数は常に Promise を返す(戻り値を自動的に Promise にラップする)
    • Promise が解決(resolve)されるまで 処理を待機できる
    • 例:return 1 と記述しても、実際は Promise.resolve(1) を返す
  • await

    awaitPromise の完了を待つ演算子

    • await を使うと、Promise が解決されるまで 後続の処理を一時停止する
    • async 関数の中でのみ使用可能

      • async でない関数内で使用すると エラーになる
    • 一般的に、async 関数と awaitセットで使われる

async/awaitとPromiseの比較

function sleep(val) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      console.log(val++);
      resolve(val);
    }, 1000);
  })
};

async function init (){
  let val = await sleep(0);
  val = await sleep(val)
  val = await sleep(val)
  val = await sleep(val)
  // この return は実行されません(上の throw により中断される)
  console.log(val)
} 

init();

同じ処理を Promise チェーンで書いた場合

sleep(0)
.then(val => sleep(val))
.then(val => sleep(val))
.then(val => sleep(val));
  • 上記の async/await の実装は、Promise セクションで紹介した処理と 同じ挙動
  • await を使うと resolve(val) の引数 val が、そのまま init 関数内の val に代入される

    • 一方、Promise では .then(val => ...) を使って 明示的に受け渡す必要
  • 両者を比較すると、async/await を使う方が よりシンプルで読みやすく、直感的に記述できる
  • なお、await を付けずに呼び出すと、戻り値は Promise オブジェクトになる

更に関数initasync関数)は Promiseを返しますのでthencatchで処理をつなげられる

//・・・上略(sleep関数)

async function init (){
  let val = await sleep(0);
  val = await sleep(val)
  val = await sleep(val)
  val = await sleep(val)
  throw new Error ('not Data')
  return val
} 

init().then(function (data) {
  console.log(data);
}).catch(function(e) {
  console.error (e)
})
  • 3回目のawait sleep(val)の後、 throw new Error ('not Data')が実行されエラーを出力される
  • この場合valは返されず、エラーが発生してcatchブロックに処理が移る
  • このように、async 関数内で発生した例外は 自動的に Promise の reject として扱われ.catch() で補足可能

try&catchで例外処理

コードの流れ

throwがある場合

try {
    console.log("start")
    throw new Error ("No Data")
    console.log("task")
} catch (e) {
    console.error(e)
} finally {
    console.log("end")
}
  • try ブロック内の console.log("start") がまず実行される
  • 次に throw new Error("No Data") により例外が発生し、その時点で try ブロックの処理は中断される
    • よって、console.log("task") は実行されない
  • 発生したエラーは catch (e) に渡され、変数 e に格納される
    • e.message には "No Data" が格納されており、console.error(e) によってエラーオブジェクトが出力される
  • 最後に finally ブロックが実行され、console.log("end") によって "end" が出力される
    • finally は、エラーの有無に関わらず必ず実行されるブロック

throwがない場合

try {
  console.log("start");
  console.log("task");
} catch (e) {
  console.error(e);
} finally {
  console.log("end");
}

  • try ブロック内の処理はすべて正常に実行される

    • console.log("start")console.log("task") の両方が出力される
  • エラーが発生しないため、catch ブロックはスキップされる
  • 最後に finally ブロックが実行され、console.log("end") が出力される

try&cachを使った例外処理

async function fetchData() {
    const res = await fetch("users.json")
    if (res.ok) {
        const json = await res.json()
        if (json.length === 0) {
            throw new Error('No Data')
        }
        return json
    }
}

const userData = async function () {
    try {
        const users = await fetchData()
        for (const user of users) {
            console.log(`I'm ${user.name}(${user.age})`)
        }
    } catch (e) {
        console.error(e)
    } finally {
        console.log("end")
    }
}

userData();

コードの流れ

fetchData(async関数)

  • この関数では、users.json というファイルからデータを取得しているが、本来は API やデータベースから取得する想定
  • 非同期で users.json ファイルからデータを取得する
  • fetch によって返された Response オブジェクトの ok プロパティが true の場合、レスポンスを .json() メソッドで JavaScript の配列に変換する
    • 取得した配列が空(json.length === 0)だった場合
      • throw new Error('No Data') によってエラーを投げ、catch ブロックに処理が移る
    • 配列にデータがある場合
      • 配列データ(json)を返す

userData(async関数)

  • try ブロック(正常にデータが取得できた場合)では

    • fetchData() の戻り値を users 変数に格納
    • for...of ループで users を順番に処理し、console.log(I’m ${user.name}(${user.age})) で各ユーザーの名前と年齢を出力
  • catch ブロック(throw new Error() によってエラーが投げられた場合)では

    • エラーオブジェクト econsole.error(e) で表示
  • finally ブロックでは

    • try または catch の処理が終了した後、必ず console.log("end") を実行して "end" を出力する

カスタムエラー

try&cachを使った例外処理のコードに下記を追加することでカスタムエラーの作成ができる

class NoDataError extends Error {
    constructor(message) {
        super(message) 
        this.name = NoDataError

    }
}

コードの流れ

  • NoDataError というクラスを、組み込みの Error クラスを継承して定義している
  • このクラスは、インスタンス化される際に message を引数として受け取る
  • super(message) は親クラス(ここでは Error クラス)のコンストラクタを呼び出すもので、エラーメッセージの内容を Error オブジェクトとして正しく扱えるようにする
  • this.name="NoDataError" によって、エラーオブジェクトの name プロパティが "NoDataError" に設定され、エラー出力時にカスタムエラー名が表示されるようになる

使用例

throw new NoDataError('not Data')

const userData = async function () {
    try {
        const users = await fetchData()
        for (const user of users) {
            console.log(`I'm ${user.name}(${user.age})`)
        }
    } catch (e) {
	    if( e instanceof NoDataError) {
        console.error(e)
       } else {
		    console.error("error!!!")
       }
    } finally {
        console.log("end")
    }
}
  • Errorを継承したNoDataError('not Data')でエラーを投げてcatchブロックに処理を移行する
  • catch ブロック内では、発生したエラーがどのクラス(インスタンス)から投げられたかを判定し、それに応じて処理を分岐している
  • エラーが NoDataError のインスタンスであれば、そのエラーオブジェクトを console.error(e) で出力
  • NoDataError 以外のエラー(たとえば TypeErrorReferenceError など)の場合は、"error!!!" という文字列を出力する

マクロタスクとマイクロタスク

マイクロタスク

マクロタスクよりも優先して実行される

例: PromisequeueMicrotask など

マクロタスク

マイクロタスクがすべて完了してから実行される
例: setTimeoutsetInterval など

  • 処理の途中で新たにマイクロタスクが追加された場合でも、マイクロタスクがすべて完了するまでマクロタスクは実行されない

ここでもう一度先ほどの関係図を見るとマクロタスク(タスクキュー)、マイクロタスク(ジョブキュー)についてもイメージができると思います。
Image from Gyazo

new Promise(function promise(resolve) {
  console.log('promise');

  setTimeout(function task1() {
    console.log('task1');
    resolve();
  });

}).then(function job1() {
  console.log('job1');

  setTimeout(function task1() {
    console.log('task2');
    queueMicrotask(function job4() {
      console.log('job4')
    })
  });

  
}).then(function job2() {
  console.log('job2');
}).then(function job3() {
  console.log('job3');
})

console.log('global end');

//コンソール出力
promise
global end
task1
job1
job2
job3
task2
job4

コードの流れ(イベントループとタスクの順序)

ここで同期タスク終了 → マクロタスクの処理へ移る

  1. task1 実行 → console.log('task1')task1
  2. resolve() 呼び出し → Promise 解決 → .then(job1) 登録 → マイクロタスク1

マイクロタスク処理開始

  1. job1 実行 → console.log('job1')job1
  2. setTimeout(task2) 登録 → マクロタスク2
  3. .then(job2) 登録 → マイクロタスク2
  4. .then(job3) 登録 → マイクロタスク3

マイクロタスク継続処理

  1. job2 実行 → console.log('job2')job2
  2. job3 実行 → console.log('job3')job3

ここでマイクロタスクが空 → 次のマクロタスク(task2)へ移る

  1. task2 実行 → console.log('task2')task2
  2. queueMicrotask(job4) 登録 → マイクロタスク4

マクロタスク終了 → マイクロタスク実行

  1. job4 実行 → console.log('job4')job4

練習問題-2

console.log('start');

setTimeout(() => {
  console.log('timeout1');

  Promise.resolve().then(() => {
    console.log('micro1');
  });

  setTimeout(() => {
    console.log('timeout2');
  });

}, 0);

Promise.resolve().then(() => {
  console.log('micro2');

  return Promise.resolve().then(() => {
    console.log('micro3');
  });
}).then(() => {
  console.log('micro4');
});

queueMicrotask(() => {
  console.log('micro5');
});

console.log('end');

上記のコードがどのように動き、どの順番でコンソールに出力されるか考えてみて下さい。
答えが決まったらスクロールして下さい。

回答

//コンソール出力
start  
end  
micro5  
micro2  
micro3  
micro4  
timeout1  
micro1  
timeout2

コードの流れ(イベントループとタスクの順序)

  1. console.log('start') → 同期 → start
  2. setTimeout(..., 0) 登録 → マクロタスク1
  3. Promise.resolve().then(...) 登録 → マイクロタスク1

    • micro2, さらに .then(() => micro3) → ネスト → マイクロタスク2
  4. .then(() => micro4) → マイクロタスク3(micro3のあと)
  5. queueMicrotask(...) 登録 → マイクロタスク4(micro5
  6. console.log('end') → 同期 → end

ここで同期タスク終了 → マイクロタスクの処理へ移る

  1. micro5(queueMicrotask) → micro5
  2. micro2micro2
  3. micro3micro2の中)→ micro3
  4. micro4micro3のあと)→ micro4

ここでマイクロタスクが空 → 次のマクロタスク(setTimeout)

  1. timeout1timeout1
  2. Promise.resolve().then(...) → マイクロタスク登録(micro1
  3. setTimeout(...) → マクロタスク登録(timeout2

マクロタスク内のマイクロタスク実行

  1. micro1
  2. timeout2

非同期の並列処理

  • Promise直列につなぐ処理は「Promiseチェーン」と呼ばれる

  • 一方、複数のPromiseを同時に実行するのが「並列処理」

    Promise.all()Promise.race() を使って複数の非同期処理を同時に扱えます。

Promise.all(反復可能オブジェクト)

function sleep(val) {
  return new Promise(function (resolve,reject) {
    setTimeout(function () {
      console.log(val++);
      resolve(val);
    }, val * 1000);
  });
}

Promise.all([sleep(1), sleep(2), sleep(3)])
	.then(function(data) {
   console.log(data);
})

//コンソール出力
1
2
3
(3) [2, 3, 4]

コードの流れ

sleep関数

  • sleep(val)Promise を返す関数
  • setTimeout を使って val 秒後に処理を実行する(例:sleep(2)なら2秒後)
  • console.log(val++) によって現在の値を出力し、次に使うためにインクリメント
  • resolve(val) により、インクリメントされた値を次の .then() に渡す

Promise.all()

  • Promise.all([...]) は、配列内のすべての Promise が「成功」するのを待ってから次の .then() に進む
  • すべてが完了すると、resolve の値が配列となって渡される

Promiseチェーンの戻り値にPromise.all([…])を渡した場合

sleep(1).then(function(val) {
  return (Promise.all([sleep(2), sleep(3), sleep(4)]))
}).then(function(val) {
  console.log(val)
  return sleep(val[1]);
}).then(function(val) {
  return sleep(val);
})

//コンソール出力
1
-------
2   
3   並列
4   
-------
(3) [3, 4, 5]
4
5

コードの流れ

  1. sleep(1) により 1秒後に 1 を出力、resolve(val) には 2 が渡る
  2. .then()Promise.all([sleep(2), sleep(3), sleep(4)]) を返す:

    • この3つの sleep()並列でスタート
    • それぞれ 2s, 3s, 4s 後に 2, 3, 4 を出力(インクリメントされるので [3, 4, 5] を返す)
  3. .then() に渡される val[3, 4, 5]。その配列の val[1] = 4 を次の sleep() に渡す
  4. sleep(4) → 4秒後に 4 を出力、val++ → 5 を返す
  5. sleep(5) → 5秒後に 5 を出力

補足

  • Promise.all([...]) で並列処理

    • 配列の各要素は Promise を返す必要がある。
    • すでに解決済みの値[1,2,3](同期処理)を渡しても内部で Promise.resolve() に包まれる(Promise.all([1, 2, 3])Promise.all([Promise.resolve(1), ...]) と同じ動作になる)ため.then() は実行される。ただし、Promise.all() の本来の目的は非同期処理の並列実行なので、通常は非同期関数の Promise を渡す
  • sleep().then() のようにつなぐと直列処理
  • 複雑なフローでも .then() チェーンをうまく使えば、直列→並列→直列 のような処理が実現できる

Promise.race(反復可能オブジェクト)

//・・・sleep関数、ボタンイベントは省略
Promise.race([sleep(1),sleep(2),sleep(3)])
.then(function (data) {
  console.log(data)
})

//コンソール出力
1
2
2
3

コードの流れ

Promise.race()

  • Promise.race([...]) は、配列内で一番最初に完了(解決または拒否)した Promise の結果に応じて、次の .then() または .catch() に進む
  • 一番早く完了した Promise の**結果(値またはエラー)**が、.then() または .catch() に渡される

Promise.allSettled(反復可能オブジェクト)

//shouldReject = false/trueでresolve/rejectを切り替え
function sleep(val, shouldReject = false) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(val++);
      if (shouldReject) {
        resolve(val);
      } else {
        reject(val);
      }
    }, val * 1000);
  });
}

Promise.allSettled([sleep(1),sleep(2),sleep(3)])
.then(function (data) {
  console.log(data)
}).catch(function(e) {
  console.error(e)
})

//コンソール出力 
resolve(val)
1
2
3
(3) [{}, {}, {}]
{status:'fulfilled', value:2}
{status:'fulfilled', value:2}
{status:'fulfilled', value:3}

reject(val)
1
2
3
(3) [{}, {}, {}]
{status:'rejected', reason:2}
{status:'rejected', reason:2}
{status:'rejected', reason:3}

コードの流れ

Promise.allSettled()

  • Promise.allSettled([...]) は、配列内のすべての Promise が完了(成功・失敗どちらでも)するのを待ち、必ず次の .then() に進む
  • すべての処理が終わると、結果は各 Promise の状態を表すオブジェクトの配列で返される
  • 各オブジェクトは成功時、 { status: ‘fulfilled’, value: 結果 }、失敗時、{ status: ‘rejected’, reason: エラー内容 } の形になっているため、status プロパティを確認して成功・失敗を判別する

最後に

いかがだったでしょうか?
この記事を読む前よりも、非同期処理について少しでも理解が深まったと感じてもらえたら嬉しいです。

すぐ完璧に理解できなくても大丈夫。
非同期処理は一度でスッと理解できるものではありません。繰り返し手を動かしながら学習することで、必ず身についていきます。

実を言うと、僕自身も2年前まではPC操作すらままならず、最近まで非同期処理が苦手すぎて「なんとなく避けて」きました。
でも、何度もつまずきながらも少しずつ向き合っていくうちに、やっと「わかった!」と思える瞬間がやってきました。
非同期処理が分かるようになると、JavaScriptでの開発がぐっと楽しくなります。
「苦手だな…」と感じていたことが「おもしろい!」に変わる瞬間を、ぜひ体験してみてください。

もし、もっとJavaScriptの本質や仕組みを深く学びたいと思った方は、参考書籍や教材にチャレンジしてみるのもおすすめです。
(ただし、ただ、決して難易度は低くないです。焦らずじっくり取り組んでくださいね!)

参考教材

【JS】ガチで学びたい人のためのJavaScriptメカニズム





Source link

Views: 0

「2024年、世界のシャチが三種に!」

📌 ニュース:

2024年から、シャチが新たに3種類に分類されます。これまで「シャチ」として知られていた種(Orcinus orca)に加え、Orcinus rectipinnus(Bigg’s killer whale)とOrcinus ater(resident killer whale)が新たに記載されました。

この研究は、アメリカ海洋大気庁のモリン氏のチームによって行われ、科学誌に発表されています。従来の認識では、シャチは単一種とされていましたが、生息地により形態や生態が異なることから、より細分化する必要性が指摘されていました。

新たな分類により、各種の保全戦略が立てやすくなり、その重要性が増します。シャチに関する理解が深まることで、今後もさらなる研究が期待されています。

  • 記事のポイントを以下のようにまとめました。

    1. シャチの新たな分類発表 🐋
      2024年から、シャチが「オルキヌス・オルカ(Orcinus orca)」に加えて、2種類の新しいシャチ「オルキヌス・レクティピヌス(Orcinus rectipinnus)」と「オルキヌス・アータ(Orcinus ater)」に分類されることになりました。これにより、シャチは3種類に増えます。📊

    2. 「種」の重要性 🌱
      シャチの新しい分類は、生物の多様性を理解し、それを保全するために重要です。異なる生態を持つ種を認識することで、適切な保護策を講じることができます。🛡️

    3. 学名の決め方 📜
      シャチの学名は、動物の性質を詳しく記載した論文を書いた研究者によって決まります。これにより、種同士の関係や分類が明確になります。🐾

    今後も新しい発見を楽しみにしましょう!📖✨


※以下、出典元
▶ 元記事を読む

Views: 0

ホラーゲーム「No, I’m not a Human」体験版登場!


🔸 ざっくり内容:

この記事は、終末世界を舞台にした新作ゲームについて紹介しています。このゲームでは、人々が夜にしか活動できない状況が描かれ、プレイヤーは深夜に訪れる人々がモンスターかどうかを見極めるという独特のタスクを担います。

背景情報

このゲームの設定は、従来のRPGやアクションゲームとは異なり、プレイヤーの判断力が求められる点が特徴です。プレイヤーは訪問者の外見や行動から、モンスターか人間かを見極める必要があります。この緊張感溢れる選択は、『Papers, Please』のような書類確認ゲームの要素を持ち合わせており、プレイヤーがリアルタイムで決断を下す緊張感を楽しむことができます。

重要な視点

  • 社会的テーマ: 人間とモンスターの境界が曖昧であることが、プレイヤーに倫理的なジレンマを投げかけます。
  • ゲームシステム: 『Papers, Please』との類似性は、単なるアクションよりも知恵を絞るゲームプレイが魅力であることを示しています。
  • 終末的な環境: 現代社会への反映が強調され、プレイヤーは物語の深いメッセージを考えるきっかけにもなります。

この新作ゲームは、緊張感あふれる選択と倫理的問いかけを通じて、プレイヤーに独自の体験を提供することを目的としています。

🧠 編集部の見解:
この記事を読んで、終末世界を舞台にしたゲームが持つ独特な魅力を改めて感じました。昼と夜で異なる活動パターンが求められる設定は、プレイヤーに緊張感と戦略的思考を促しますよね。特に、深夜に訪れる人々が果たしてモンスターかどうかを見極めるシステムは、まるで『Papers, Please』のような社会的選択をゲームに取り入れたとも言えます。

例えば、『Papers, Please』は、難民の受け入れや国家の法を遵守することをテーマにしており、プレイヤーは倫理的な選択を迫られました。これと似て、今回のゲームでも「見逃すことができない選択」がゲームの緊張感を生む要素になっていますね。

社会的な影響としては、こうしたゲームがプレイヤーに倫理観や判断力を養う手助けをするという点が挙げられます。特に、終末的な設定では人間の本質や社会の脆さが反映されるため、プレイヤーが自らの価値観を再考する機会となるかもしれません。

豆知識として、近年では「終末後」の世界を描くゲームが増えており、特に『The Last of Us』や『Fallout』シリーズはその代表例です。これらの作品も、ただのサバイバルではなく、人間関係や道徳が大きなテーマになっている点が共通しています。こうしたゲームを通じて、プレイヤーが感じる思索や感情は、リアルな社会でも大切なことかもしれませんね。

  • キーワード: 終末世界


『Papers, Please』をAmazonで探す

ss_e7eb595b45971e1a4ad6dc9562f142566ab9e

終末世界のゲームをAmazonで探す

ss_e7eb595b45971e1a4ad6dc9562f142566ab9e

モンスターゲームをAmazonで探す

ss_e7eb595b45971e1a4ad6dc9562f142566ab9e


※以下、出典元
▶ 元記事を読む

Views: 0

相次ぐ“弁当が届かない詐欺”1都3県20社以上で被害…男の免許証コピー入手 同一人物“高本”が詐欺営業か 警察が捜査

仕出し弁当の営業を受け契約したものの弁当が届かないというトラブルが相次いでいます。 店長: これ1万5000円払って、ばっ …

Views: 0

「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる



 配信イベント「PC Gaming Show 2025」で,Ubisoftは新作タイトル「アノ 117: パックスロマーナ」PC / PS5 / Xbox Series X|S)を,2025年11月13日に発売すると発表した。
 予約注文も受付開始している。

画像ギャラリー No.001のサムネイル画像 / 「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる




 本作は「アノ」シリーズの最新作で,舞台は全盛期のローマ帝国。その一角にある青い海に囲まれた小島ラティウムで,プレイヤーは総督として貿易や外交をし,ときには陸地や海上での戦争に乗り出す。
 ローマ皇帝に忠誠を誓うか。反乱を企てるか。すべては選択次第だ。

画像ギャラリー No.002のサムネイル画像 / 「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる 画像ギャラリー No.003のサムネイル画像 / 「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる
画像ギャラリー No.004のサムネイル画像 / 「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる 画像ギャラリー No.005のサムネイル画像 / 「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる
画像ギャラリー No.006のサムネイル画像 / 「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる 画像ギャラリー No.007のサムネイル画像 / 「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる
画像ギャラリー No.008のサムネイル画像 / 「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる 画像ギャラリー No.009のサムネイル画像 / 「アノ 117: パックスロマーナ」,2025年11月13日に発売決定。全盛期のローマ帝国で,青い海に囲まれた小島の総督となる

※記事に使用している画像はすべて配信画面のスクリーンショットです

「アノ 117: パックスロマーナ」公式サイト

4Gamer「PC Gaming Show 2025」記事一覧

YouTube「PC Gaming Show 2025」



続きを見る


🧠 編集部の感想:
「アノ 117: パックスロマーナ」の発売が決定し、非常に楽しみです。全盛期のローマ帝国が舞台という設定に魅力を感じます。プレイヤーの選択によって展開が変わるストーリーが期待でき、戦略フリークとしては見逃せないタイトルです!

Views: 0

「レールボーン」:列車で巡る希少植物収集サバイバル!

🔸 ざっくり内容:

『レールボーン』: 新たなクラフトサバイバルゲーム

背景情報
2025年6月9日に開催されるオンラインイベント「PC Gaming Show 2025」で、新たなクラフトサバイバルゲーム『レールボーン』が発表されることが決まりました。本作は、日本語にも対応し、早期アクセスが近々開始される予定です。

ゲームの概要
『レールボーン』は、プレイヤーが資源を集めて列車を組み立て、生き延びることを目的としたゲームです。プレイヤーは列車を拠点にして、希少な植物を収集し、生態系を再生させながら、線路が張り巡らされた世界を冒険します。最大4人でのオンラインマルチプレイも可能です。

目的と探索要素
プレイヤーは、遺棄された開拓者の拠点や謎に包まれたモノリスを調査し、この惑星に隠された真実を解き明かすことが求められます。本作は、サバイバル要素に加えて、環境再生や探索が重要なテーマとなっています。

このゲームは、新たな冒険を求めるゲームファンにとって、大きな注目を集めること間違いありません。

🧠 編集部の見解:
『レールボーン』は、プレイヤーが列車を組み立てながら生き延びるサバイバルゲームです。このゲームの面白い点は、ただ生き残るだけでなく、希少な植物を集めて生態系を再生するという環境要素もあるところです。これは、プレイヤーに自分の行動が環境に与える影響を考えさせる良い機会を提供してくれます。

### 感想
自分が日常生活で環境について考えることは少ないですが、ゲームを通じてその重要性を感じるのは新鮮です。特に、資源を集めて列車を作り上げるという要素は、創造性を発揮できる部分でもあり、同時に戦略的思考を必要とします。

### 関連事例
最近では、環境問題への意識が高まる中、ゲームの中でも「環境保護」や「サステイナビリティ」をテーマにしたものが増えてきています。例えば、他のサバイバルゲームでも「資源管理」を通じてプレイヤーに選択の重要性を教えるものが多く見られます。『レールボーン』もその流れの一部として、エコ意識を取り入れた斬新なアプローチと言えます。

### 社会的影響
今後、こうした環境意識をテーマとしたゲームが増えることで、幅広い層の人々が自然や生態系の重要性を再認識するきっかけとなるかもしれません。特に、若い世代には効果的なイメージ教育のツールとなるでしょう。

### 背景と豆知識
ゲームの中で取り入れられている「生態系再生の要素」は、実際の自然保護活動ともリンクしています。近年では、ゲームを通じて環境教育を行う取り組みも増えており、オーストラリアの「Eco-Game」などがその一例です。プレイヤーが楽しみながら環境に対する理解を深められるのは素晴らしいことですね。

このように、ただのゲームではなく、教育的要素を持った『レールボーン』は、楽しみながらも何かを学べる貴重な体験を提供してくれそうです。早期アクセスが楽しみですね!

  • キーワード: レールボーン


※以下、出典元
▶ 元記事を読む

Views: 0

「FF7R」と「FF16」Xbox版、今すぐ楽しめる!

📌 ニュース:
「FF7R」と「FF16」のXbox版が発表されました。

配信番組「Xbox Games Showcase 2025」で、
「FINAL FANTASY VII REMAKE INTERGRADE」と
「FINAL FANTASY XVI」のXbox版が紹介されました。

Xbox版「FF7R インターグレード」は今冬発売予定、
「FF16」は本日6月9日よりプレイ可能です。

シリーズファンには嬉しいニュースですね!
詳細は公式サイトや動画でチェックしてください。

  • 以下のポイントをお知らせいたします!✨

    1. 新たな展開! 🎮
      「FINAL FANTASY VII REMAKE INTERGRADE」と「FINAL FANTASY XVI」のXbox版が発表されました。これにより、Xboxユーザーも名作を楽しむことができます!

    2. 本日からプレイ可能!
      「FF16」は本日よりXbox Series X|SとXbox PCでプレイ可能です。すぐに冒険に出かけるチャンスです!

    3. 「FF7R」は今冬発売予定! ❄️
      Xbox版「FF7R インターグレード」は今冬に発売予定です。楽しみに待ちましょう!

    これからも新情報に注目です!🎉


FINAL FANTASY VII REMAKE INTERGRADE をAmazonで探す
FINAL FANTASY XVI をAmazonで探す
Xbox をAmazonで探す


※以下、出典元
▶ 元記事を読む

Views: 0

トランスコスモス、CX向上の新ツール登場!

📌 ニュース:
トランスコスモスは、新たに「trans-CX Survey(COMX診断)」を提供開始しました。このサービスは、企業が自社と競合の顧客体験(CX)の急所を可視化し、コミュニケーション体験を評価するものです。

COMXフレームワークを用いて、Webアンケートを通じてCXの主要指標を測定。自社と競合を比較分析し、業界内での位置付けや収益向上のための強み・弱みを明確にします。

さらに、購入前後のカスタマージャーニーに沿って診断を実施し、DX推進やAI活用のポイントを特定。その結果を迅速に報告し、改善策へのインサイトを提供します。オプションで、改善策の立案支援も受けられます。

  • トランスコスモスが提供する「trans-CX Survey(COMX診断)」のポイントを3つご紹介します!✨

    1. 顧客体験の可視化 📊
      自社と競合の顧客体験(CX)の急所を可視化し、どの部分が改善の余地があるかを明確にします。

    2. カスタマージャーニーに基づく分析 🛤️
      購入前後のカスタマージャーニーに沿って診断を行い、デジタル化やAI活用のポイントを特定します。

    3. 改善策の迅速な提案 ⚙️
      診断結果をもとに、迅速にインサイトを提供し、具体的な改善策を計画するためのデスクリサーチも利用可能です。

    ぜひ、このサービスを活用して、顧客体験を向上させてください!🌟


trans-CX Survey をAmazonで探す

COMX をAmazonで探す

カスタマージャーニー をAmazonで探す


※以下、出典元
▶ 元記事を読む

Views: 0

「Kenkoが新サイズ追加! ブラックミストNo.05登場」

📌 ニュース:
株式会社ケンコー・トキナーは、
丸型フィルター「ブラックミスト No.05 N」に
86mm径と95mm径を追加し、
6月6日に発売しました。

86mmは26,000円、95mmは33,500円です。
このソフトフィルターは
微細な黒い拡散材を光学ガラスで挟み込み、
19〜95mmまでの全24サイズが揃います。

ハイライトとシャドー部のコントラストを抑え、
柔らかな描写を実現。
特有の光を拡散し、
映画のような雰囲気を演出します。

「05」はブラックミストシリーズの中間的な位置付けで、
効果の強さは「1」が最も強く、
「ブラックミストプロテクター」が最も弱いです。

  • Kenkoの「ブラックミスト No.05」に関するポイントを以下のようにまとめました✨

    1. 新モデルの登場📸

      • ケンコー・トキナーが「ブラックミスト No.05 N」の86mmと95mmを新たに発売しました。希望小売価格はそれぞれ26,000円と33,500円です。
    2. 柔らかな描写🌌

      • このフィルターは光の拡散材を使用しており、ハイライトとシャドーのコントラストを抑え、映画のような柔らかな描写を実現します。特に夜景や街の灯りに効果的です。
    3. 多様なラインアップ📏
      • 新サイズの追加により、ブラックミストシリーズは19〜95mmまでの全24サイズを展開しています。中間的な位置づけの「05」は、より強い効果の「1」と、弱い効果のプロテクターの中間です。

    ぜひ、フィルターを使って新しい撮影体験を楽しんでください!📷


※以下、出典元
▶ 元記事を読む

Views: 0