AJAXにおける非同期処理の基本概念と応用

同期処理と非同期処理

同期処理はコードを順次実行し、結果を待ってから次の処理に進みます。非同期処理は実行後に結果を待たず、後続のコードを継続実行し、将来完了時にコールバック関数で結果を返します。

const total = 5 + 3;
console.log(total); // 8

setTimeout(() => {
  console.log('非同期処理完了');
}, 1000);

document.querySelector('#submit').addEventListener('click', () => {
  console.log('ボタンクリック');
});

document.body.style.backgroundColor = '#f0f0f0';
console.log('処理継続');

出力順序: 8 → 処理継続 → 非同期処理完了(1秒後)

コールバック地獄

コールバック関数内でのネストが深くなると、可読性低下やエラー処理困難などの問題が発生します。

fetchProvinceData().then(provinceResult => {
  const province = provinceResult.regions[0];
  updateElement('.province', province);
  
  fetchCityData(province).then(cityResult => {
    const city = cityResult.areas[0];
    updateElement('.city', city);
    
    fetchDistrictData(province, city).then(districtResult => {
      updateElement('.district', districtResult.locations[0]);
    });
  });
});

Promiseの連鎖処理

then()メソッドが新たなPromiseを返す特性を利用し、非同期処理を直列的に連結します。

const initialPromise = new Promise((resolve) => {
  setTimeout(() => resolve('東京都'), 1000);
});

initialPromise
  .then(province => {
    console.log(province);
    return new Promise(resolve => {
      setTimeout(() => resolve(`${province} - 新宿区`), 1000);
    });
  })
  .then(area => {
    console.log(area);
  });

async/awaitの活用

async関数内でawaitを使用すると、Promiseの解決を待機し、同期処理のような記述が可能になります。

async function fetchRegionData() {
  const provinceRes = await fetch('api/provinces');
  const province = provinceRes.regions[0];
  
  const cityRes = await fetch(`api/cities?province=${province}`);
  const city = cityRes.areas[0];
  
  const districtRes = await fetch(`api/districts?province=${province}&city=${city}`);
  const district = districtRes.locations[0];
  
  updateUI(province, city, district);
}

fetchRegionData();

エラー処理

try/catch構文で非同期処理中のエラーを捕捉します。

async function loadData() {
  try {
    const response = await fetch('api/data');
    if (!response.ok) throw new Error('APIエラー');
    return response.json();
  } catch (error) {
    console.error('データ取得失敗:', error);
    return null;
  }
}

イベントループの仕組み

JavaScriptの実行モデルでは、メインスレッドがタスクキューからコールバックを取り出し実行します。

console.log('開始');
setTimeout(() => console.log('タイマー1'), 0);
Promise.resolve().then(() => console.log('プロミス1'));
setTimeout(() => console.log('タイマー2'), 100);
console.log('終了');

出力順序: 開始 → 終了 → プロミス1 → タイマー1 → タイマー2

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

非同期処理は実行タイミングにより分類されます。

  • マクロタスク: setTimeout, setInterval, UIイベント
  • マイクロタスク: Promise, queueMicrotask
console.log('スクリプト開始');
setTimeout(() => console.log('マクロタスク'), 0);
Promise.resolve().then(() => console.log('マイクロタスク'));
console.log('スクリプト終了');

出力順序: スクリプト開始 → スクリプト終了 → マイクロタスク → マクロタスク

Promise.allによる並列処理

複数のPromiseを同時実行し、全て成功した場合に結果をまとめて取得します。

const fetchUser = fetch('api/users');
const fetchProducts = fetch('api/products');
const fetchOrders = fetch('api/orders');

Promise.all([fetchUser, fetchProducts, fetchOrders])
  .then(responses => {
    const [userRes, productsRes, ordersRes] = responses;
    console.log('全データ取得完了');
  })
  .catch(error => {
    console.error('いずれかのリクエストが失敗:', error);
  });

タグ: AJAX 非同期処理 Promise async/await イベントループ

5月17日 06:50 投稿