JavaScriptの再入門編・後編
繊細なアニメーションやインタラクティブなページを作成したり、サーバーと連携したりとJavaScriptを理解することは、現在のウェブ開発においては重要なスキルの一つです。ここでは、JavaScript(ES6以降に追加された機能も含めて)の基本を復習していきましょう!という事で再入門記事を書いてみました。
記事が長すぎたので前後編に分けました。
7. 非同期処理
非同期処理はJavaScriptの重要な概念であり、このセクションでは非同期処理の概念と、その実現方法であるコールバック、Promise、async/awaitについて学びます。
let delayedGreeting = new Promise((resolve, reject) => {
setTimeout(() => resolve("Hello, World!"), 1000);
});
async function greet() {
let greeting = await delayedGreeting;
console.log(greeting);
}
greet(); // "Hello, World!" (1秒後に表示)
コールバック
JavaScriptはシングルスレッドで動作するため、一度に1つのタスクしか処理できません。しかし、ネットワークリクエストやタイマーのような時間がかかる操作を行う場合、その完了を待つと全体の処理が止まってしまいます。そこでJavaScriptでは非同期処理を利用して、待ち時間中に他のタスクを進めることができます。
ここで重要となるのが「コールバック」です。コールバックは、特定のタスクが完了した後で実行するための関数を指します。以下に非同期処理の一例として、JavaScriptの組み込み関数であるsetTimeout
を使用したコードを示します。
console.log("Start");
setTimeout(function() {
console.log("Callback executed");
}, 3000); // 3秒後に実行
console.log("End");
上記のコードを実行すると、”Start”を表示した後、”End”を表示し、最後に3秒後に”Callback executed”を表示します。setTimeout
関数は非同期で動作し、指定した時間が経過した後にコールバック関数を実行します。しかし、その間もメインのプログラムは止まることなく実行を続けます。
非同期処理は、時間のかかる処理をバックグラウンドで行いつつ、メインのプログラムをブロックせずに動作させるための重要な機能です。しかし、コールバックを使った非同期処理は、複数の操作を順序正しく行う場合やエラーハンドリングを行う場合には複雑になりやすいという欠点があります。そのため、現在ではPromiseやasync/awaitといったより現代的な非同期処理のパターンがよく使われます。
Promise(プロミス)
JavaScriptの非同期処理をより扱いやすくするための一つの方法が「Promise」です。Promiseは非同期操作の終了を表すオブジェクトで、その操作が成功したか失敗したかを表します。操作が成功すれば「解決(fulfilled)」状態となり、失敗すれば「拒否(rejected)」状態となります。
以下に非同期処理を行う関数をPromiseを使って書き換えた例を示します。
let myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Operation successful"); // 非同期操作が成功
}, 3000);
});
myPromise.then(
value => { console.log(value); }, // Operation successful
error => { console.log(error); }
);
上記のコードでは、new Promise
を使ってPromiseを作成しています。Promiseのコンストラクタは引数として1つの関数(エクゼキュータ)を受け取ります。このエクゼキュータ関数にはresolve
とreject
という2つの引数があり、非同期操作の成功または失敗をそれぞれ表します。
このPromiseでは、setTimeout
で3秒後にresolve
関数を呼び出しています。つまり、このPromiseは3秒後に「解決」状態になります。then
メソッドは、Promiseが解決または拒否された時に呼び出されるコールバック関数を登録します。第1引数は解決時のコールバック、第2引数は拒否時のコールバックです。
このようにPromiseを使うと、非同期処理の状態をより直感的に扱うことができ、また複数の非同期処理を連続したり並列に行ったりすることが容易になります。ただし、Promiseも多くの非同期処理を扱うとなるとコードが複雑になりがちで、特にエラーハンドリングが難しくなります。そのため、現代のJavaScriptではasync/awaitというさらに直感的な非同期処理のパターンが導入されています。
async/await
JavaScriptのasync
とawait
は非同期処理を行う際の構文で、非常に読みやすいコードを書くことができます。これらはES2017で導入され、Promiseのシンタックスをより明確で直感的なものにする役割を果たします。
まずasync
キーワードは、関数が必ずPromiseを返すことを保証します。これにより、その関数の中で非同期処理を安全に行うことができます。
async function asyncFunction() {
return "Hello, World!";
}
asyncFunction().then(console.log); // Hello, World!
次にawait
キーワードは、Promiseがresolve(解決)またはreject(拒否)されるまで、関数の実行を一時停止します。await
はasync
関数内でのみ使用できます。
async function fetchAndDecode(url) {
let response = await fetch(url);
let data = await response.json();
return data;
}
上記のコードでは、fetch
関数により返されるPromiseがresolveされるまで待ち、その結果をresponse
に代入します。次に、response.json()
により返されるPromiseがresolveされるまで待ち、その結果をdata
に代入します。
このようにasync/await
を使うと、非同期処理をあたかも通常の同期処理のように書くことができます。これにより、コードは非常に読みやすくなります。しかし、async/await
はあくまでPromiseのシンタックスシュガー(構文糖衣)であり、基礎となるPromiseの仕組みを理解していなければ、混乱を招く可能性もあります。そのため、Promiseとその動作を理解した上でasync/await
を使用することをおすすめします。
8. ES6以降の新機能
JavaScriptは進化し続けており、新しいバージョンでは便利な機能が追加されています。特に、ECMAScript 6(ES6)以降では、アロー関数、デストラクチャリング、async/awaitなどの重要な機能が追加されました。
アロー関数:
これは関数を短く書くための新しい構文です。また、アロー関数は自身のthis値を親スコープから継承します。
// 通常の関数
function add(a, b) {
return a + b;
}
// アロー関数
let add = (a, b) => a + b;
console.log(add(1, 2)); // 3
クラス構文:
これはプロトタイプベースの継承をより直感的に書くための新しい構文です。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}
let alice = new Person("Alice", 20);
alice.greet(); // Hello, my name is Alice.
Promise(プロミス):
これは非同期処理をより直感的に書くための新しい構文です。成功値またはエラー値を持つことができます。
let promise = new Promise((resolve, reject) => {
resolve("Promise resolved");
});
promise.then((value) => {
console.log(value); // Promise resolved
});
デストラクチャリング:
オブジェクトや配列から値を取り出し、個々の変数に代入することができます。
let person = {name: 'Alice', age: 20};
// デストラクチャリングを使わない場合
let name = person.name;
let age = person.age;
// デストラクチャリングを使う場合
let {name, age} = person;
console.log(name, age); // Alice 20
テンプレートリテラル:
これは文字列内に変数を埋め込むための新しい構文です。
let name = 'Alice';
// テンプレートリテラルを使わない場合
let greeting = 'Hello, ' + name + '!';
// テンプレートリテラルを使う場合
let greeting = `Hello, ${name}!`;
console.log(greeting); // Hello, Alice!
スプレッド演算子:
これは配列やオブジェクトを展開するための新しい構文です。
let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5, 6]; // スプレッド演算子によりarr1の要素が展開される
console.log(arr2); // [1, 2, 3, 4, 5, 6]
async/await:
これはPromiseベースの非同期処理をより直感的に書くための新しい構文です。
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Promise resolved"), 1000);
});
// async/awaitを使わない場合
promise.then((value) => {
console.log(value); // Promise resolved
});
// async/awaitを使う場合
async function getPromiseValue() {
let value = await promise;
console.log(value); // Promise resolved
}
getPromiseValue();
9. Node.js入門
Node.jsは、ブラウザの外でJavaScriptを実行するための環境で、Google ChromeのV8 JavaScriptエンジン上に構築されています。これにより、JavaScriptを用いてサーバーサイドの開発が可能になりました。従来、JavaScriptは主にブラウザ内(クライアントサイド)で実行されていましたが、Node.jsの登場により、データベース操作、ファイルシステムの操作、サーバー間通信など、従来のサーバーサイド言語であるPython、Ruby、Java、PHPなどが行ってきたようなタスクをJavaScriptで行えるようになりました。
Node.jsの主な特性は以下の通りです:
- 非同期I/O:
Node.jsでは、データベースへのクエリ、ファイルの読み書き、ネットワークリクエストなどのI/O操作が非同期で行われます。これにより、I/O操作がブロックされることなく、他の処理を同時に行うことが可能です。これはNode.jsが高性能なアプリケーションを構築する上での重要な要素です。 - モジュールシステム:
Node.jsにはCommonJSというモジュールシステムが組み込まれており、コードを再利用可能なパーツ(モジュール)に分割することが可能です。また、npm(Node Package Manager)というパッケージマネージャを通じて、他の開発者が作成したモジュールを利用することも可能です。 - イベントドリブン:
Node.jsはイベントドリブンなアーキテクチャを採用しており、特定のイベント(例えば、ユーザーからのリクエストの受信やデータベースからの応答の受信など)が発生した際に呼び出される関数(コールバック)を設定することができます。
以下に簡単なHTTPサーバーの作成例を示します:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, World!\n');
});
server.listen(3000, '127.0.0.1', () => {
console.log('Server running at http://127.0.0.1:3000/');
});
上記のコードは、最も基本的なHTTPサーバーを作成するNode.jsのコードです。http
モジュールを使用してHTTPサーバーを作成し、そのサーバーが3000番ポートでリクエストを待ち受けるように設定しています。サーバーにリクエストがあった場合、”Hello, World!”というレスポンスを返します。
これらの特性により、Node.jsはリアルタイムアプリケーション(例えば、チャットアプリケーションやゲームサーバーなど)、シングルページアプリケーションのバックエンド、APIサーバー、データストリーミングアプリケーション、コマンドラインツールなど、多岐にわたるアプリケーションを作成するための選択肢となっています。
あとがき
ここまででJavaScriptの再入門編は終了です。この記事を通して、JavaScriptの基本的な構文から関数、オブジェクト、そして非同期処理まで幅広い内容をおさらいしました。
次のステップでは、より深くJavaScriptに触れ、さらに先へ進んでください!