본문 바로가기

Dart

[Dart] Future 쉽게 배우고 사용하는 방법(예제)

반응형

 

저는 제가 이번에 좀 팠다고 생각했는데 그게 또 아닌것도 같습니다.

그럼 다시 한번 파보겠습니다. 미래의 바쁜 저를 위해서 가장 알아듣기 쉬운 글로 작성합니다.

왜 사용할까요? 

비동기 작업을 사용하면 다른 작업이 완료될 때까지 기다리는 동안 프로그램이 다른 작업을 완료할 수 있습니다. 주로 이럴 때 사용됩니다. 

  • 네트워크를 통해 데이터를 가져올 때
  • 데이터베이스에서 데이터 가져올 때 쓸 때
  • 파일에서 데이터 읽어올 때

이런 비동기 계산은 일반적으로 결과를 Future로 제공하거나 결과에 대해 여러 값이 있는 경우 스트림으로 사용합니다. 이러한 프로그래밍은 비동기성을 필요로하며 초기 비동기화를 수용하기 위해 다른 일반 다트 함수들도 비동기화되어야 합니다. 

 

비동기 결과와 상호 작용하려면 async 및 await 키워드를 사용합니다. 대부분 비동기 함수는 본질적으로 비동기 계산에 의존하는 다트 함수일 뿐입니다. 어렵게 생각하지 맙시다! 

 

예시 코드

예시 코드를 살펴보면서 알아볼까요? 먼저 네트워크를 통해 데이터를 가져온다고 가정하고 Future를 사용하지 않았을 때입니다. 

// This example shows how *not* to write asynchronous Dart code.

String createOrderMessage() {
  var order = fetchUserOrder();
  return 'Your order is: $order';
}

Future<String> fetchUserOrder() =>
    // Imagine that this function is more complex and slow.
    Future.delayed(
      const Duration(seconds: 2),
      () => 'Large Latte',
    );

void main() {
  print(createOrderMessage());
}


//결과
Your order is: Instance of '_Future<String>'

 

위의 예제에서 fetchUserOrder()가 최종적으로 생성하는 값을 출력하지 못하는 이유는 다음과 같습니다. 

  • fetchUserOrder()는 사용자의 주문을 설명하는 문자열을 제공하는 비동기 함수입니다. 
  • 사용자의 주문을 받으려면 createOrderMessage()에서 fetchUserOrder()를 호출하여 주문이 완료될 때까지 기다려야 합니다. createOrderMessage()는 fetchUserOrder()가 완료될 때까지 기다리지 않으므로 createOrderMessage()는 fetchUserOrder()가 제공하는 문자열 값을 가져오지 못합니다. 
  • 대신 createOrderMessage()는 완료될 보류 중인 작업, 즉 완료되지 않은 Future<String>을 가져옵니다. createOrderMessage()가 사용자의 주문을 설명하는 값을 가져오지 못하지 때문에 예제에서 콘솔에 'Large Latte'를 인쇄하지 못하고 대신 Your order is: Instance of '_Future<String>'가 출력됩니다. 
synchronous operation: 동기화 작업은 완료될 때까지 다른 작업이 실행되지 못하도록 차단합니다. 
synchronous function: 동기화 가능은 동기화 작업만 수행합니다.
asynchronous operation: 일단 시작된 비동기 작업은 완료되기 전에 다른 작업을 실행할 수 
asynchronous function: 적어도 하나의 비동기 연산을 수행하며 동기 연산을 수행할 수도 있습니다. 

Future의 정의

Future클래스의 인스턴스입니다.  future는 비동기 연산의 결과를 나타내며 미완료 또는 완료 두가지 상태를 가질 수 있습니다. 

 

Uncompleted

비동기 함수를 호출하면 완료되지 않은 미래를 반환합니다. future는 함수의 비동기 연산이 끝나거나 오류를 일으키기를 기다리고 있습니다. 

 

Completed

비동기 작업이 성공하면 future는 값을 가집니다. 그렇지 않으면 오류와 함께 완료됩니다. 

 

Completing with a value

future의 타입인 Future<T>의 값으로 완료됩니다. 예를 들어 Future<String>유형은 문자열 값을 생성합니다. 값을 만들어내지 못한다면 Future<void>가 됩니다. 

 

Completing with an error

함수에 의해 수행되는 비동기 연산이 어떤 이유로든 실패하면 오류와 함께 완료됩니다. 

 

그럼 이제 비동기 키워드를 사용해서 결과를 얻는 방법을 알아봅니다. 

 

Quick review:

 

A Future<T> instance produces a value of type T.

If a future doesn’t produce a usable value, then the future’s type is Future<void>.

A future can be in one of two states: uncompleted or completed.

When you call a function that returns a future, the function queues up work to be done and returns an uncompleted future.

When a future’s operation finishes, the future completes with a value or with an error.

Key terms:

 

Future: the Dart Future class.

future: an instance of the Dart Future class.

 

Working with futures: async and await

 

async와  await 키워드는 비동기 함수를 정의하고 결과를 사용할 선언적 방법을 제공한다.

비동기 함수를 정의하려면 함수 본문 앞에 async를 넣습니다. 

await키워드는 async 함수에서만 작동합니다. 

void main() async { ··· }

 

만약 함수에 선언된 반환 유형이 있는 경우 유형을 Future<T>로 업데이트합니다. 함수가 명시적으로 값을 반환하지 않으면 반환 유형은 Future<void>입니다. 

Future<void> main() async { ··· }

 

이제 비동기 함수를 만들었으니 await키워드를 사용하여 완료될 때까지 기다릴 수 있습니다. 

 

아래 두가지 예를 통해 async와 await를 생성해봅니다. 

 

String createOrderMessage() {
  var order = fetchUserOrder();
  return 'Your order is: $order';
}

Future<String> fetchUserOrder() =>
    // Imagine that this function is
    // more complex and slow.
    Future.delayed(
      const Duration(seconds: 2),
      () => 'Large Latte',
    );

void main() {
  print('Fetching user order...');
  print(createOrderMessage());
}

// 결과값
Fetching user order...
Your order is: Instance of '_Future<String>'

 

Future<String> createOrderMessage() async {
  var order = await fetchUserOrder();
  return 'Your order is: $order';
}

Future<String> fetchUserOrder() =>
    // Imagine that this function is
    // more complex and slow.
    Future.delayed(
      const Duration(seconds: 2),
      () => 'Large Latte',
    );

Future<void> main() async {
  print('Fetching user order...');
  print(await createOrderMessage());
}

//결과값
Fetching user order...
Your order is: Large Latte

 

위의 예제를 통해 비동기 예제는 동기예제와 세가지 측면에서 다른 걸 확인할 수 있습니다. 

createOrderMessage()의 반환타입이 Future<String>로 변경됩니다.

createOrderMessage()와 main() 함수 본문 앞에 async 키워드가 존재합니다. 

await 키워드가  fetchUserOrder()와 createOrderMessage()를 호출하기 전에 작성합니다. 

 

Future<void> printOrderMessage() async {
  print('Awaiting user order...');
  var order = await fetchUserOrder();
  print('Your order is: $order');
}

Future<String> fetchUserOrder() {
  // Imagine that this function is more complex and slow.
  return Future.delayed(const Duration(seconds: 4), () => 'Large Latte');
}

void main() async {
  countSeconds(4);
  await printOrderMessage();
}

// You can ignore this function - it's here to visualize delay time in this example.
void countSeconds(int s) {
  for (var i = 1; i <= s; i++) {
    Future.delayed(Duration(seconds: i), () => print(i));
  }
}



//결과

Awaiting user order...
1
2
3
4
Your order is: Large Latte

 

========================================================

 

The following exercise is a failing unit test that contains partially completed code snippets. Your task is to complete the exercise by writing code to make the tests pass. You don’t need to implement main().

 

To simulate asynchronous operations, call the following functions, which are provided for you:

 

Exercise: Practice using async and await

 

Part 1: reportUserRole()

Add code to the reportUserRole() function so that it does the following:

 

Returns a future that completes with the following string: "User role: <user role>"

Note: You must use the actual value returned by fetchRole(); copying and pasting the example return value won’t make the test pass.

Example return value: "User role: tester"

Gets the user role by calling the provided function fetchRole().

 

Future<void> printUserRole() async{

  print('Awaiting user role... ');

  var user = await reportUserRole();

  print('this is user: $user');

}

 

Future<String> reportUserRole()async{

  return Future.delayed(const Duration(seconds: 4), ()=> 'User role: tester');

}

 

void main() async{

  print('this is part 1 test');

  await printUserRole();

}

 

 

 

 

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  const MyHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);
  
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
 String test = '감자';

Future<void> printUserRole() async{
  print('Awaiting user role... ');
  var user = await reportUserRole();
  print('@@@@@@@@ printUserRole user : ${user} / test: ${test}@@@@@@@');
  test = await test;
  print('@@@@@@@@ printUserRole user : ${user} / test: ${test}@@@@@@@');
}

Future<String> reportUserRole()async{
  print('@@@@@@@@ reportUserRole  test: ${test}@@@@@@@');
  return Future.delayed(const Duration(seconds: 4), ()=> '고구마');
}

  @override
  void initState(){
    print('@@@@@@@@ initState test: ${test}@@@@@@@');
    print('this is part 1 test');
  }

  @override
  Widget build(BuildContext context) {
    print('@@@@@@@@ build test: ${test}@@@@@@@');
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
            onPressed: ()async{
               await printUserRole();
            },
              child: (
              Text('test button')
              ),
            ),
            const Text(
              'Hi this is my test for Future',
            ),
            FutureBuilder(
            future: reportUserRole(),
            builder: (ctx, snapshot){
              print('@@@@@@@@ FutureBuilder test: ${test} | snapshot.toString(): ${snapshot.data.toString()}@@@@@@@');    
              return Container(child: (Text(snapshot.data.toString()??'')));
            }
            ),
            Container(child: (Text(test))),
          ],
        ),
      ),
    );
  }
}

 

 

반응형