Mobile/Dart

Dart에서 비동기 작업을 처리하는 방법 - 1 (Future)

펭귄알 2025. 3. 15. 16:16

 

대표적인 싱글 스레드 언어인 JavaScript는 비동기 작업을 처리하기 위해 Web API와 Event Loop를 활용합니다. JavaScript 엔진 내부에서 직접 비동기 작업을 처리하는 것이 아니라, 작업을 Web API에 위임한 후 **이벤트 루프(Event Loop)**를 통해 결과를 받아와 실행하는 방식이죠.

Dart도 이와 유사한 방식으로 비동기 작업을 처리합니다. Dart 내부에서 모든 작업을 실행하는 것이 아니라, Dart VM과 Event Loop를 활용하여 비동기 작업을 관리합니다.

공식 문서에서도 Event Queue 에 대해 설명하고 있으며, Dart의 이벤트 루프 동작 방식에 대한 자세한 내용은 아래 링크에서 확인할 수 있습니다. Dart Event Loop

 

Dart의 동시성

Isolate를 사용하여 멀티 프로세서 코어에서 병렬 코드를 실행하세요.

dart-ko.dev

Dart의 이벤트 큐와 마이크로태스크 큐

이벤트 큐에는 I/O 작업(네트워크 요청, 파일 읽기/쓰기), 사용자 입력(마우스 클릭, 터치 이벤트), UI 렌더링, 타이머, Isolate 간 메시지 등이 포함됩니다.

Dart에서는 Microtask Queue 가 존재하며, 이는 일반 이벤트 큐보다 먼저 실행되는 가벼운 비동기 작업을 처리하는 역할을 합니다. 즉, 현재 실행 중인 코드가 완료된 직후, 다음 이벤트 루프가 실행되기 전에 마이크로태스크 큐가 먼저 실행됩니다.

Dart의 이벤트 루프는 다음과 같은 순서로 실행됩니다:

  1. 현재 실행 중인 코드가 끝나면 마이크로태스크 큐를 먼저 확인하고 모든 마이크로태스크를 실행합니다.
  2. 마이크로태스크가 모두 처리된 후, 이벤트 큐에서 다음 작업을 실행합니다.
  3. 위 과정이 반복되며, 이벤트 큐가 비어 있으면 모든 작업이 완료됩니다.

Future

Dart에서 Future는 비동기 작업을 처리하는 기본적인 방법입니다. Future는 실행 위치에 따라 이벤트 큐(Event Queue)에서 실행되는 Future와 마이크로태스크 큐(Microtask Queue)에서 실행되는 Future로 나뉩니다.

Event Queue에서 실행되는 Future

이벤트 큐에서 실행되는 Future는 일반적인 비동기 작업을 처리할 때 사용됩니다.

  • Future(() => ...)
  • Future.delayed(Duration, () => ...)

로딩 버튼 처리

아래 예제에서는 버튼을 클릭하면 2초 동안 로딩 상태를 유지한 후 다시 버튼이 활성화됩니다.

import 'dart:async';
import 'package:flutter/material.dart';

class LoadingButton extends StatefulWidget {
  @override
  _LoadingButtonState createState() => _LoadingButtonState();
}

class _LoadingButtonState extends State<LoadingButton> {
  bool isLoading = false;

  void _handleButtonPress() {
    setState(() => isLoading = true); 

    Future.delayed(Duration(seconds: 2), () {
      setState(() => isLoading = false); 
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Loading Button Example')),
      body: Center(
        child: ElevatedButton(
          onPressed: isLoading ? null : _handleButtonPress,
          child: isLoading
              ? CircularProgressIndicator()
              : Text("Load Data"),
        ),
      ),
    );
  }
}

Event Queue에는 우리가 클릭한 버튼에 대한 외부 정보들이 큐에 추가되었고, 각 정보를 순차적으로 Event Loop에 전달합니다, 또한 Queue는 FIFO이기 때문에 먼저들어온 정보가 먼저 처리됩니다.

버튼이 작동하는 방식

  1. 버튼을 클릭하면 _handleButtonPress()가 실행됩니다.
  2. setState(() => isLoading = true);를 호출하여 로딩 상태를 활성화합니다.
  3. Future.delayed(Duration(seconds: 2), () { ... })를 사용하여 2초 후 로딩 상태를 해제합니다.
  4. UI는 isLoading 값을 기반으로 로딩 상태를 반영합니다.

이벤트 큐에서 실행되는 Future이므로 마이크로태스크보다 실행 순서가 뒤로 밀릴 수 있습니다.


Microtask Queue에서 실행되는 Future

마이크로태스크 큐에서 실행되는 Future는 즉시 실행되며, 일반 이벤트 큐보다 우선순위가 높습니다.

  • Future.microtask(() => ...)
  • Future.sync(() => ...)

아래 코드에서는 버튼을 클릭하면 마이크로태스크 큐에서 즉시 실행되도록 변경하였습니다.

import 'dart:async';

void _handleButtonPress() {
  setState(() => isLoading = true); // 로딩 시작

  scheduleMicrotask(() {
    setState(() => isLoading = false); // 즉시 로딩 종료
  });
}

실행 과정

  1. 버튼을 클릭하면 _handleButtonPress()가 실행됩니다.
  2. setState(() => isLoading = true);를 호출하여 로딩 상태를 활성화합니다.
  3. scheduleMicrotask(() { setState(() => isLoading = false); }); 실행 → 즉시 로딩 상태가 해제됨.

Stream

Dart에서 비동기처리 하는 방법은 Stream도 있는데요, 이 내용은 아래 포스트를 참고하시면 추가적인 정보를 얻을수있습니다!

https://my0366.tistory.com/10

 

[Dart] Stream 뽀개기

이번 글에서는 Dart의 Stream에 대해 알아보겠습니다.우선 Stream을 다루기 전에 비동기 프로그래밍에 대해 간단히 소개하고 넘어가겠습니다.비동기 프로그래밍특정 작업이 완료될 때까지 프로그램

my0366.tistory.com

 

또한 Dart에서는 추가적으로 Future, Stream에 말고 Isolate API도 추가적으로 지원하는데 이 내용에 관해서는 다음 포스팅에서 작성해보겠습니다. 오늘도 빡코딩!