비동기 프로그래밍이란?

 

일단 동기(Syncronous)는 무엇이고 비동기(Asyncronous)는 무엇인가?

  -> 동기는 blocking되는 코드 -> 이전 명령이 안끝나면 다음명령이 실행 되지 않음

  -> 비동기는 blocking되지 않는 코드 -> 이전 명령이 안끝나도 다음명이 실행 됨 

 

왜 동기적인 작업이 문제가 될까? 예를들어

  -> 파일을 불러올때

  -> 데이터베이스에 데이터를 쓸때

  -> 네트워크로부터 데이터를 불러올때

등등 여러 케이스에서 한 작업이 끝날때까지 뒤의 모든 작업이 기다리게 된다. 

이럴 경우 시간적으로 손해가 크기때문에 동기화된 작업은 추천 되지 않는다 

 

그렇다면 비동기는? 작업이 완료될때까지 기다리지 않고 다음 작업을 바로 실행함(병렬적)

그렇다면 결과값은? 작업이 마무리되면 그때 데이터를 받음 

 

dart에서는 dart.async 라이브러리를통해 Future, Stream기능, 비동기 프로그래밍을 지원하게 된다 

 

Future을 알아보자

Future을 사용할 때 반환 되는 타입은 Future타입이다. Future타입안에 데이터타입이 명시 된다. 

Future<int> number = Future.value(1);
Future<String> name = Future.value('스트링');
Future<bool> isTrue = Future.value(true);

 

언제 쓰일까?

  -> 하나의 작업에 대해 이벤트가 한번 발생하는 단일 비동기 작업에 사용된다. 

void main(){
  int seconds = 2;
  print('start');
  Future.delayed(Duration(seconds: seconds), () {
    print("waited $seconds");
  });
  print('done');
}

// 출력되는 결과
/*
  start
  done
  waited 2
*/

 

비동기 작업을 동기화 처럼 사용하고자 할때(순서를 지켜서 실행해야할때) async, await키워드를 사용해서 코드를작성한다 

void main() async{
  int seconds = 2;
  print('start');
  await Future.delayed(Duration(seconds: seconds), () {
    print("waited $seconds");
  });
  print('done');
}

// 출력되는 결과
/*
  start
  waited 2
  done
*/

 

하지만 Future은 단 한번만 결과값을 받게 된다. 그래서 Stream을 사용한다

 

Stream에 대해 알아보자

제일 큰 특성은 시간에 따라 연속적인 데이터 흐름을 제공한다

연속적으로 데이터를 받을 수 있기 때문에 프로그램이 임의로 Stream을 종료하지 않는다

사용자가 Stream을 시작하고 끝을 내야한다.

 

예제로 알아보기전에 yield와 listen() method를 알아야 한다 

 

yield는 값을 방출하도록 하는 키워드 -> return 과 비슷하다고 생각하면 된다

listen()은 yield로 방출되는 값을 얻기위해 사용되는 메서드다

 

예제로 알아보자 

참고로 Stream을 적용한 함수에는 async* 키워드를 달아주어야한다 async가 아니다 

아래 예제는 입력받은 숫자부터 0까지 1초를 기다리면서 출력한는 예제이다 

Stream<int> emitNUmbers(int first) async* {
  for(var i = first; i >= 0; i--){
    yield i;
    
    await Future.delayed(Duration(seconds:1));
  }
}

void main() {
  emitNumbers(10).listen((number) {
    print(number);
  }
}

/*
  10
  9
  8
  7
  6
  5
  4
  3
  2
  1
  0
  이 차례대로 출려 됨 
*/

 

참고로 고차함수로 체이닝하여 사용할 수도 있다고 한다. 

체이닝을 사용하면 특정 값만 걸러내서 받을 수도 있을거같다. 

728x90

함수형 프로그래밍에 대해서 배워보자

 

첫번째 정의는 함수의 연속으로 프로그램을 구성하는 것이라고 생각하면 된다고 한다 

Method Chaining이라고도 한다고 한다 

  - ' . ' 을사용해서 여러개의 함수를 하나로 연결하는 방식

 아래 코드와 같이 여러 method 를 연속적으로 사용하는 방식

var result = number.abs().toString();

 

두번째 정의는 가변적인 데이터의 사용을 최소화하여 프로그램을 구성하는 방식이라고 하는데 

예제로 살펴보자 

아래와 같이 매개변수에만 의존하는 함수의 형태를 Pure FUnction(순수 함수)라고 한다 

int add(int a, int b){
  return a + b;
}

또다른 예제로는 아래와 같은 함수도 있다

잘 보면 매개변수를 의존하여 값을 계산하기 때문이다(스코프 안에서만 변경한다)

int getTotal(List<int> numbers) {
  int result = 0;
  for (var number in numbers){
   result += numbers;
  }
  return result;
}

 

함수형 프로그래밍에서 많이 사용하는 함수에는 2가지가 있다

  - 형변환 함수(Type Casting Function)

    -> 특정 데이터를 다른 타입의 데이터로 변경하는 함수(toString(), int.parse('')같은 함수)

  - 고차 함수(Higher-order function)

    -> map(), where(), reduce(), fold(), any(), every(), takeWhile()등  여러가지 종류가 있으며 중요한 몇가지 함수를 알아보자

   

      -> map()은  Collection타입의 데이터 각요소에 특정 함수를 적용한 새로운 collection을 반환하는 함수이다(shallow copy)

      -> 예제는 아래 코드와 같다. List의 모든 요소에 특정 작업을 해서 새로운 List를 반환하고 있다. 

//map(([매개변수]) { return [동작] });
List<String> fruitList = ['a', 'b', 'c'];
var delicious =. ruitList.map((fruit) {
  var word = '맛있는';
  word += fruit;
  reutrn word;
});
print(delicious); // 맛있는 a, 맛있는 b, 맛있는 c 출력

//map(([매개변수]) => [동작]); 
List<String> fruitList = ['a', 'b', 'c'];
var delicious = fruitList.map((fruit) => '맛있는 + $fruit');
print(delicious); // 맛있는 a, 맛있는 b, 맛있는 c 출력

     

      -> where()은 Collection 요소중 참인 조건에 부합되는 요소만 필터링한 컬렉션을 반환한다 

      -> 예제는 아래와 같다. Return안에 bool값을 판별할 수 있는 식이 존재해야 한다

      -> 자매품으로 firstWhere(), lastWhere()등이 있다만 얘네들은 참인 요소가 없을때는 에러를 뱉는다(stateError)

// where(([매개변수]) { return [조건식]; }
List<int> numbers = [1,2,3,4,5,6,7];
var result = numbers.where((number) {
  return number > 5;
});
print(result); // 6,7 출력

 

      -> reduce()는 collection에 있는 요소들을 하나의 값으로 결합하는 함수

      -> 예제는 아래와 같다, 리스트를 순회하면서 앞에 값을 뒤에 값에 계속더하면서 진행된다

      -> 특징은 collection의 데이터타입 과 같은 타입으로만 반환할 수 있고 데이터가 비어있을경우 에러가 발생한다(stateError)

// reduce(([매개변수1], [매개변수2]) { return [동작] };
List<int> numbers = [1,2,3,4,5];
var result = numbers.reduce((a, b) {
  return a + b
});
print(result); // 15 출력

 

      -> fold()는 reduce에서 초기값이 추가된다. reduce()와는 다르게 초기값이 초기값으로 세팅된다. 

      -> 특이사항으로는 다른 타입으로도 반환이 가능하고 요소가 없어도 에러가 발생 안한다고 한다. 

// fold(초기값, ([매개변수1], [매개변수2]) { return [동작] });
// 앞에 초기값이 추가된다

List<int> numbers = [1,2,3,4,5];
var result = numbers.fold(0, (a, b) {
  return a + b
});
print(result); // 15 출력

 

 

      -> any()는 주어진 collection중 하나라도 조건을 만족시 true를 반환한다(where과 비슷하지만 bool을 반환한다)

      -> every()는 collection에서 모든 요소가 맞아야 true가 반환된다 

      -> takeWhile()은 collection에서 true인 요소가 나오는 동안에는 요소를 반환하고 false로 나오는 요소부터는 무시하는 기능이다

      -> skipWhile()은 collection에서 true인 요소가 나오는 동안에는 요소를 스킵하고 false로 나오는 순간부터의 요소를 모두 반환한다

 

Method Chaining은 위 고차함수를 잘 조합해서 쓰는게 핵심이다 

map() + where(),

where() + reduce(),

where() + map() + fold() 등등

Chaining을 통해 collection에서 필요한 여러 작업을 짧은 코드수로 끝낼 수 있다

728x90

+ Recent posts