본문 바로가기
IT/Flutter

<Dart> What is Future and how to use it?

by 세계 최고의 AI Engineer_naknak 2023. 3. 2.

Future is Dart grammer. yes maybe...

The reason I choose thiis, it's kind of hard to understand and use.

So through this poster, I'm gonna study it deeply.

There are two references.

https://dart.dev/codelabs/async-await

 

Asynchronous programming: futures, async, await

Learn about and practice writing asynchronous code in DartPad!

dart.dev

https://velog.io/@jintak0401/FlutterDart-%EC%97%90%EC%84%9C%EC%9D%98-Future-asyncawait

 

Flutter/Dart 에서의 Future, async/await

Flutter 와 Dart 를 공부하면서 깨달은 Future, async / await 에 대한 설명과 고민에 대한 답을 작성한 포스트입니다.

velog.io

 

Basically, I understand Futter in Korean and explain this in English

 

Asynchronous programming: futures, async, await

What is Future?

This is for asynchronous programming.

Asynchronous programming is that when you execute A program, before main thread finish A program you can execute B program. This mean u execute and u don't have to wait until it finished, and u can run other program.

"(of two or more objects or events) not existing or happening at the same time." this is from dictionary.

 

Okay. 

Why asynchronous code matters 

The reason asynchronous is important!

 

Asynchronous operations let your program complete work while waiting for another operation to finish. Here are some common asynchronous operations:
  • Fetching data over a network.
  • Writing to a database.
  • Reading data from a file.
Such asynchronous computations usually provide their result as a Future or, if the result has multiple parts, as a Stream. These computations introduce asynchrony into a program. To accommodate that initial asynchrony, other plain Dart functions also need to become asynchronous.

*(accommodate 수용하다, plain "평지, 순수한" 이라는 뜻인데 여기선 일반적인 다트 함수라고 이해하면 좋을 거 같다)

 

근데 영어로 하니까 딱히 쓸 게 없다... ㅎㅎ

Yes!

To interact with these asynchronous results, you can use the async and await keywords. Most asynchronous functions are just async Dart functions that depend, possibly deep down, on an inherently asynchronous computation.

 

Anyway, if you want to use asynchronous functions, just use async and await key words.

 

자 한번 공식 문서에 나온 잘못된 사용예를 보도록 하죠!

Example: Incorrectly using an asynchronous function

The following example shows the wrong way to use an asynchronous function (fetchUserOrder()). Later you’ll fix the example using async and await. Before running this example, try to spot the issue – what do you think the output will be?

// 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());
}

공식 문에서 잘 설명되어 있는 걸 간단하게 설명하도록 할게요!

Here’s why the example fails to print the value that fetchUserOrder() eventually produces:
  • fetchUserOrder() is an asynchronous function that, after a delay, provides a string that describes the user’s order: a “Large Latte”.
  • To get the user’s order, createOrderMessage() should call fetchUserOrder() and wait for it to finish. Because createOrderMessage() does not wait for fetchUserOrder() to finish, createOrderMessage() fails to get the string value that fetchUserOrder() eventually provides.
  • Instead, createOrderMessage() gets a representation of pending work to be done: an uncompleted future. You’ll learn more about futures in the next section.
  • Because createOrderMessage() fails to get the value describing the user’s order, the example fails to print “Large Latte” to the console, and instead prints “Your order is: Instance of ‘_Future<String>’”.

ayync 와 await를 사용하지 않았기 때문에 createOrderMessage() 를 호출 했을 때 fetchUserOrder() 가 끝났을 때까지 기다리지 못해서 “Your order is: Instance of ‘_Future’” 이 녀석을 출력한다는 말입니다!

 

In the next sections you’ll learn about futures and about working with futures (using async and await) so that you’ll be able to write the code necessary to make fetchUserOrder() print the desired value (“Large Latte”) to the console.

그리고 이제 future을 사용해서 (async, await도) 어떻게 원하는 값을 얻을 때까지 기다리는지를 알아보겠다고 하네요.

이건 개념 정리 같아요

Key terms:

  • synchronous operation: A synchronous operation blocks other operations from executing until it completes.
  • synchronous function: A synchronous function only performs synchronous operations.
  • asynchronous operation: Once initiated, an asynchronous operation allows other operations to execute before it completes.
  • asynchronous function: An asynchronous function performs at least one asynchronous operation and can also perform synchronous operations.

비동기는 아까 말했듯이 A라는 녀석이 끝나기 전에 다른 B라는 녀석이 수행될 수 있다는 개념이예요.

뭐 operation이랑 function이 어떤 부분이 크게 다른지는 모르겠어요.. 저는 둘 다 함수라고 알고 있는데 ㅎㅎ...

설명을 보면 function이 좀 더 큰 개념 같아요.

 

What is a future?

A future (lower case “f”) is an instance of the Future (capitalized “F”) class. A future represents the result of an asynchronous operation, and can have two states: uncompleted or completed.

자 그럼 이제 future에 대해서 알아볼까요? future은 비동기 operation의 결과를 나타낸다고 하네요. 그리고 비완료나 완료 상태가 될 수 있다고도 해요.

Note: Uncompleted is a Dart term referring to the state of a future before it has produced a value

비완료는 다트 용어인데 값을 제공하기 전에 future의 상태라고 하네요!

 

Uncompleted

When you call an asynchronous function, it returns an uncompleted future. That future is waiting for the function’s asynchronous operation to finish or to throw an error.

Completed

If the asynchronous operation succeeds, the future completes with a value. Otherwise, it completes with an error.

 

완료와 비완료에 대한 설명이네요.

완료는 비동기 operation이 성공적이로 끝나서 future에 값이 return 됐을 때 혹은 error가 return 됐을 때를 이야기하고

비완료는 아까 말했듯이 비동기 함수를 불렀을 때 그 함수의 비동기 operation이 끝나거나 error를 던지기를 기다리는 상태라고 생각하면 좋을 거 같아요!

 

Completing with a value

A future of type Future<T> completes with a value of type T. For example, a future with type Future<String> produces a string value. If a future doesn’t produce a usable value, then the future’s type is Future<void>.

 

완료되서 값을 return 하는 경우에 Future<T>로 future을 선언해주네요. T는 값의 타입을 의미하고 String이나 여러가지가 될 수가 있어요. return 값이 없으면 void로 선언하면 될 거 같아요.

 

Completing with an error

If the asynchronous operation performed by the function fails for any reason, the future completes with an error.

어떤 이유로 값을 못 받아오면(수행이 안되면) error를 던지면서 끝난대요.

 

2023/03/02 시간이 늦어서 일단은 여기서 포스팅을 마무리할게요!

네, 노트북 SSD가 갑자기 인식이 안되서... 교체하고 다시 윈도우를 깔았는데

사용자 이름이 HANSUNG인거예요. 그래서 이거 사용자 폴더명을 바꾸려고 발광을 하다가 현재 로컬 계정에서 로그아웃이 안되서 부팅할 때마다 컴퓨터가 초기화되고 관리자권한으로 실행할 수 있는게 단 하나도 없어진 상태예요...

멘탈이 깨졌지만 마음을 다잡고 포스트 마무리하겠습니다.

Example: Introducing futures

Future<void> fetchUserOrder() {
  // Imagine that this function is fetching user info from another service or database.
  return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}

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

What is print first? 저기 fetchUserOrder가 먼저 호출됐지만 delayed에 의해서 2초 뒤에 실행되며 비동기 성질에 따라서 Fetching user order가 먼저 출력된 뒤 Large Latte가 출력됩니다.

 

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.

정리하면 이렀다고 합니다. Key terms가 좀 신박하네요... Future은 다트 Future 클래스이고 future은 다트 Future 클래스의 인스턴스라고 합니다.

*queues(꼬리-프랑스어라네요 ㅋㅋ, 줄을 서다)

이제 이걸 어떻게 사용하는 지 알아볼까요?

Working with futures: async and await

The async and await keywords provide a declarative way to define asynchronous functions and use their results. Remember these two basic guidelines when using async and await:

  • To define an async function, add async before the function body:
  • The await keyword works only in async functions.

future은 async과 await를 통해 구현됩니다! 그리고 async과 await 라는 키워드는 비동기 함수를 정의하고 그 결과를 사용하는 declarative 뉘앙스적인? 방법 이라고 하네요!

2가지 기억해야 할 것은, async 함수를 정의하기, 선언하기 전에 async을 함수 body 전에 추가하라는 것.

또 await 는 오직 async 함수에서만 동작한다는 것. 이라네요! 여기서 in은 안에서 라기보다는 과 함께로 해석하는 게 더 적절한 거 같아요.

왜냐면 다음 예시를 보면 그렇거든요.

void main() async { ··· }

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

print(await createOrderMessage());

이렇게 async과 await가 사용되는 걸 확인할 수 있죠!

개그인지는 모르겠지만 The only differences are highlighted in the asynchronous example, which—if your window is wide enough—is to the right of the synchronous example. 

동기와 비동기의 차이점은 비동기가 동기의 예제에 오른쪽에 있는 거라고 하니 신경쓰지 않아도 될 거 같아요!

 

Key terms:

  • async: You can use the async keyword before a function’s body to mark it as asynchronous.
  • async function: An async function is a function labeled with the async keyword.
  • await: You can use the await keyword to get the completed result of an asynchronous expression. The await keyword only works within an async function.

await를 사용하면 비동기 표현에서 완료된 결과를 가질 수 있다네요.

넵 위에 설명한 것과 동일하죠. 

 

Example: Execution within async functions

실제 사용 예제를 볼까요? 저는 이게 되게 어렵더라구요 ㅠㅠ

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));
  }
}

위와 같은 코드를 실행하면

이런 결과가 나와요

하지만 2,3번 줄을 바꾸면! 

var order = await fetchUserOrder();
print('Awaiting user order...');

이런 결과가 나와요!

왜 그런지 생각해볼까요 >ㅡ<

main() 에서 countSeconds() 가 호출이 되요! Future.delays() 이기 때문에 1초마다 1,2,3,4 가 차례대로 호출되겠죠!

그리고 1이 호출되기전 그러니까 print(1)이 되기전에 printOrderMessage()가 호출되요!

첫번째 코드에서는 바로 Awaiting user order가 print 되겠죠? 그다음 fetchUserOrder() 가 await로 호출되었네요?

즉, 이제 fetchUserOrder()가 완료되기전까지 기다려준다. 라는 뜻이예요! 이런 과정이 진행되는 사이에 counSeconds()가 1~4까지 print하고 fetchUserOrder()는 4초 뒤에 Large Latte 라는 String 값을 반환하게 되요! 그게 변수 order로 들어가고 마지막으로 Your order is : Large Latte가 출력 되는 겁니다!!!

2, 3번 줄을 바꾸면 방금 설명한 것과 같은 이유로 fetchUserOrder가 String 값을 반환 받게 되는 4초의 시간 후에 그 다음 코드로 넘어갈 수 있게 되서 바로 위에 있는 결과가 나오는 거예요!!

 

Exercise: Practice using async and await

요기 문제가 있는데 해당 문제는 사이트로 가셔서 풀어보시기 바랍니다^^

대입 문제라 굉장히 쉽워요! (문제 읽고 이해하는 게 더 오래 걸렸어요 ㅎㅎ..)

 

// Part 1
// You can call the provided async function fetchRole()
// to return the user role.
Future<String> reportUserRole() async {
  TODO('Your implementation goes here.');
  var userRole = await fetchRole();
  return 'User Role : $userRole';
}

// Part 2
// Implement reportLogins here
// You can call the provided async function fetchLoginAmount()
// to return the number of times that the user has logged in.
Future<String> reportLogins() async {
  var loginTimes = await fetchLoginAmount();
  return 'Total number of logins: $loginTimes';
}

 

Handling errors

To handle errors in an async function, use try-catch:

에러가 날 경우 아래와 같이 잡으래요!

try {
  print('Awaiting user order...');
  var order = await fetchUserOrder();
} catch (err) {
  print('Caught error: $err');
}

Within an async function, you can write try-catch clauss the same way you would in synchronous code.

*clauss 가 뭘까요? 구조인가... 모르겠어요 ㅠ

암튼 async 함수 내에서 try-catch 문으로 에러를 잡을 수 있답니다.

 

이 예시에 대한 제 코드

// Implement changeUsername here
Future<void> changeUsername() async {
  try {
    var userName = await fetchNewUsername();  
    print('User Name : $userName');
  } catch (err) {
    print(err.toString());
  } 
}

공식문서에서 나온 답

Future<String> changeUsername() async {
  try {
    return await fetchNewUsername();
  } catch (err) {
    return err.toString();
  }
}

 

Exercise: Putting it all together

후우... 마지막 예제입니다... 화이팅!!!

It’s time to practice what you’ve learned in one final exercise. To simulate asynchronous operations, this exercise provides the asynchronous functions fetchUsername() and logoutUser():

지금까지 배운 걸 다 써먹어 보라고 하네요!

async, await, try-catch 만 기억하면 문제 없이 해결할 수 있을 거 같아요!

 

// Part 1
addHello(String user) {
  print('Hello $user');
}

// Part 2
// You can call the provided async function fetchUsername()
// to return the username.
Future<String> greetUser() async {
  var userName = await fetchUsername();
  return addHello(userName);
}

// Part 3
// You can call the provided async function logoutUser()
// to log out the user.
Future<String> sayGoodbye() async {
  try {
    await logoutUser();
    return 'Thanks, see you next time';
  } catch (err) {
    return 'Oh, There is a problem!';
  }
  
}

이게 제가 짠 코드예요 part 3에서 logoutUser에 대한 리턴 값을 변수에 담아서 sayGoodbye()에 대한 return 값으로 넘겨주어야 하는데 제가 문제를 이해를 잘못해서 저런 코드가 나왔답니다.

 

String addHello(String user) => 'Hello $user';

Future<String> greetUser() async {
  var username = await fetchUsername();
  return addHello(username);
}

Future<String> sayGoodbye() async {
  try {
    var result = await logoutUser();
    return '$result Thanks, see you next time';
  } catch (e) {
    return 'Failed to logout user: $e';
  }
}

이게 solution이구요. Part 1에서 애로우 함수를 쓰니까 코드가 훨씬 깔끔한 느낌이 드네요!

 

네! 이번 포스트에서는 Future에 대해서, 또 동기와 비동기, 그걸 어떻게 쓰는지 + 예제까지 알아봤습니다!

매번 느끼는 거지만 공식문서를 보면서 공부하는 건 정말 쉽지 않네요!

하지만 하나 하나 포스팅해가면서 조금씩 앞으로 나아가는 거 같아서 굉장히 뿌듯합니다!

감사합니다!

'IT > Flutter' 카테고리의 다른 글

<Dart> Streams 비동기 프로그래밍  (0) 2023.03.16
<Dart> Effective Dart: Documentation  (0) 2023.03.06
<Flutter> What is State?  (0) 2023.02.15
<Flutter> Scaffold란?  (0) 2023.02.14
<Dart> 효과적인 Dart: Style  (0) 2023.02.13

댓글