В чем разница между асинхронностью в C# и JavaScript?

«В чем разница между асинхронностью в C# и JavaScript?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Основное различие лежит в фундаментальных моделях выполнения: C# использует многопоточность, а JavaScript — однопоточный цикл событий (event loop).

C# (модель на основе задач и потоков):

  • Основана на Task/Task<T> и ключевых словах async/await.
  • Асинхронные операции обычно выполняются в пуле потоков (ThreadPool), что позволяет реально использовать несколько ядер CPU для параллельной работы.
  • await приостанавливает выполнение метода, освобождая поток для других задач, но не блокирует поток целиком.
  • Управление контекстом синхронизации через ConfigureAwait(false).
public async Task<int> FetchDataAsync()
{
    // I/O-операция (например, HTTP-запрос) не блокирует поток
    var data = await httpClient.GetStringAsync("https://api.example.com/data");
    // После await выполнение может продолжиться в другом потоке из пула
    return ProcessData(data);
}

JavaScript (однопоточная модель с циклом событий):

  • Основана на Promises и async/await (синтаксический сахар над Promises).
  • Весь код выполняется в одном потоке. Асинхронность достигается за счет отложенного выполнения колбэков через очередь микрозадач (microtask queue) и макрозадач (task queue).
  • await приостанавливает выполнение async-функции, но не блокирует главный поток, который продолжает обрабатывать другие события из очереди.
  • Нет понятия потоков для параллельного выполнения JavaScript-кода (Web Workers — это отдельные окружения).
async function fetchData() {
    // fetch возвращает Promise. await «приостанавливает» функцию, но не поток.
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    // Пока ждем ответа сети, поток может отрисовывать UI или обрабатывать клики.
    return data;
}

Ключевые отличия:

  1. Потоки: C# использует многопоточность, JS — один поток.
  2. Параллелизм: В C# несколько Task могут выполняться физически параллельно на разных ядрах. В JS асинхронные операции выполняются последовательно в одном потоке, но без блокировки на операциях ввода-вывода.
  3. Цель: В C# асинхронность часто используется как для I/O-bound (ожидание сети/диска), так и для CPU-bound операций (с использованием Task.Run). В JS асинхронность в первую очередь решает проблему неблокирующего I/O в однопоточной среде.