동기와 비동기

맘비·2023년 5월 30일
0

CS / Web

목록 보기
10/10

동기 통신

지연 없이 실시간으로 일어나는 양방향 통신

클라이언트에서 Request를 보냈을 경우, Reponse를 받는 형식의 통신으로, request를 처리하는 과정에서 response를 돌려주기 전까지 다른 request들은 block이 되며, request를 렌더링 해서 전송을 해줄 때까지 기다린다.

그렇기 때문에 동기 통신은 코드의 실행 순서를 보장하고 결괏값을 예측하기 쉽게 하지만 작업이 완료될 때까지 기다려야 하므로, 실행 시간이 길어질 수 있다.

비동기 통신

비동기 통신은 동기 통신과 다르게 request를 보냈을 경우, 해당 request에 대한 response를 처리하지 못했음에도 다른 request를 받을 수 있는 방식의 통신이다. Request를 보낸 thread는 request를 보내고 response를 기다리지 않는 것이다.

즉, 코드의 실행 흐름이 일시 중지되지 않고, 작업이 완료되는 시점을 기다리지 않는 대신 작업이 완료되면 콜백 함수를 호출하여 결괏값을 전달한다.

그렇기 때문에 비동기 처리는 실행 시간이 더 짧아지며, 작업이 완료될 때까지 기다리지 않아도 되므로 다른 작업을 처리할 수 있다. 하지만 코드의 실행 순서가 보장되지 않기 때문에, 결괏값을 예측하기 어렵다.

JavaScript 비동기 처리


1. Promise

자바스크립트 비동기 처리에 사용되는 객체이다. 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결괏값을 나타낸다.

서버에서 데이터를 다 받아 오기도 전에 화면에 데이터를 표시하려고 하면 오류가 발생하거나 빈 화면이 뜨는데, 이를 해결하기 위한 방법 중 하나 이다.

Promise의 상태 3가지

  • Pending(대기) : 비동기 처리가 아직 완료되지 않은, 이행도 거부도 하지 않은 초기 상태 new Promis() 를 호출하면 대기 상태가 된다.
  • Fulfilled(이행) : 비동기 처리가 완료되어 Promise가 결과 값을 반환해준 상태 콜백 함수의 인자 resolve를 실행하면 이행 상태가 되고, 이행 상태가 되면 then()을 이용해 처리 결과 값을 받을 수 있다.
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태 콜백 함수의 인자 reject를 호출하면 실패 상태가 되고, 실패 처리의 결과 값(실패 이유)를 catch로 받을 수 있다.

Promise 형식

function getData(callback) {
  return new Promise(function(resolve, reject) {
    $.get('url 주소', function(response) {
      // 작업이 성공하면 resolve(), 실패하면 reject 호출
			if (true) {
	      resolve(response);
	    } else {
	      reject(response);
	    }
    });
  });
}

// getData()의 실행이 끝나면 호출되는 then()과 catch()
getData()
	.then(tableData => {
		// resolve() 결과값이 여기로 전달됨
	  console.log(tableData); // $.get()의 reponse 값이 tableData에 전달됨
	})
	.catch(error => {
    // 비동기 작업이 실패하면 실행
    console.error(error);
  });

promise 실습

function synchronousFunction() {
    console.log('동기적 처리 -----------');
    console.log('step 1');
    console.log('step 2');
    console.log('step 3');
}

synchronousFunction();
console.log('done');

function asynchronousFunction(message) {
    console.log(`${message} step 1`);
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log(`${message} step 2`);
        resolve();
      }, 2000);
    })
    .then(() => {
      console.log(`${message} step 3`);
    });
}

console.log('비동기적 처리 -----------')
var firstMsg = "비동기 1번";
asynchronousFunction(firstMsg).then(() => {
    console.log(`${firstMsg} done`);
});

var secondMsg = "비동기 2번"
asynchronousFunction(secondMsg).then(() => {
    console.log(`${secondMsg} done`);
});

// 실행 결과: 
// 동기적 처리 -----------
// step 1
// step 2
// step 3
// done
// 비동기적 처리 -----------
// 비동기 1번 step 1
// 비동기 2번 step 1
// 비동기 1번 step 2
// 비동기 1번 step 3
// 비동기 1번 done
// 비동기 2번 step 2
// 비동기 2번 step 3
// 비동기 2번 done

2. Async Await

자바스크립트 비동기 처리 패턴 중 가장 최근 나온 문법

기존의 비동기 처리 방식인 콜백 함수와 Promise의 단점을 보완하고 개발자가 읽기 좋은 코드를 작성할 수 있게 도와준다.

await는 Promise가 처리될 때까지 함수 실행을 기다리게 만들고, Promise가 처리되면 그 결과와 함께 실행이 재개된다. Promise가 처리되길 기다리는 동안엔 엔진이 다른 일(다른 스크립트를 실행, 이벤트 처리 등)을 할 수 있기 때문에, CPU 리소스가 낭비되지 않는다.

Async Await의 기본 문법

먼저 함수의 앞에 async라는 예약어를 붙이고, 함수의 내부 로직 중 HTTP 통신을 하는 비동기 처리 코드 앞에 await를 붙인다. 주의할 점은, 비동기 처리 method가 꼭 Promise 객체를 반환해야 await가 의도한 대로 동작한다는 것이다. 그렇기에 일반적으로 await의 대상이 되는 비동기 처리 코드는 Axios 등 Promise를 반환하는 API 호출 함수이다. (만약 Promise가 아닌 것이라면, Promise로 감싸 반환한다.) 또한 당연한 이야기이지만, await는 async 함수가 아닌 일반 함수에서는 사용할 수 없다.

async function 함수명() {
  await 비동기_처리_메서드_명();
}

Async Await 사용 예시

// async await로 비동기 처리를 해 job 목록의 데이터를 가져옴
async getJobList(){
      try{
        const results = await SchedulerApi.scheduleList(this.paging.nowPage);
        this.jobs = results.result.jobs;
        this.paging = results.result.paging;
      }catch(error){
        console.log(error);
      }
    }

C# 비동기 처리


Task와 Async Await

1. Task

비동기, 병렬 처리를 지원하는 클래스이다. Task 클래스가 나오기 이전에는 Thread 클래스를 사용해 비동기 작업을 처리했다. Task 클래스를 사용하여 다른 Thread에서 실행되는 작업을 생성하고 그 작업이 완료되면 결과를 처리할 수 있다.

2. Async Await

  • async는 비동기 메서드를 정의할 때 사용된다. async 메서드는 1개 이상의 await를 가질 수 있고, 하나도 없는 경우라도 컴파일은 가능하지만 경고 메세지가 표시된다. async 메서드의 return 타입은 대부분 Task인데, 예를 들어 string일 경우, 컴파일러가 자동으로 Task으로 변환해준다.
  • await는 일반적으로 Task 객체와 함께 사용된다. 그 외에는 GetAwaiter()라는 메서드를 갖는 클래스(ex. Awaitable)이면 함께 사용 가능하다. await는 비동기 작업이 완료될 때까지 메서드의 실행을 일시 중지하고, 완료되기를 기다린다. 그 때, UI Thread(또는 Worker Thread)가 정지되지 않고 사용자가 다른 작업을 처리할 수 있도록 해준다. 해당 Task가 완료 되면 await 바로 다음 실행문부터 실행을 계속한다.

Task.Delay()를 사용하여 일정 시간이 지난 후에 작업이 완료될 수 있도록 지연 시킬 수 있다. 또한 await를 사용하여 비동기 작업이 완료될 때까지 기다리고, 완료된 작업의 결과를 처리할 수 있다.

private void button37_Click(object sender, EventArgs e)
{
    TaskTest task = new TaskTest();
    task.DoWorkAsync();
}

public class TaskTest
{
    public async Task DoWorkAsync()
    {
        Debug.WriteLine("작업을 시작합니다.");

        // Task.Delay() 메소드를 사용하여 1초 후에 작업이 완료될 수 있도록 지연시킴
        await Task.Delay(1000);

        Debug.WriteLine("1초가 지났습니다. 첫 번째 작업이 완료되었습니다.");

        // 다른 작업을 수행
        await DoOtherWorkAsync();

        Debug.WriteLine("두 번째 작업이 완료되었습니다.");
    }

    public async Task DoOtherWorkAsync()
    {
        // 다른 스레드에서 실행되는 작업을 생성합니다.
        var task = Task.Run(() =>
        {
            Debug.WriteLine("다른 작업을 실행합니다.");
        });

        // 작업이 완료될 때까지 기다립니다.
        await task;
    }
}

// 실행 결과:
// 작업을 시작합니다.
// 1초가 지났습니다. 첫 번째 작업이 완료되었습니다.
// 다른 작업을 실행합니다.
// 두 번째 작업이 완료되었습니다.

Task.WhenAll()를 사용하여 여러 개의 Task 객체를 받아서 모든 작업이 완료될 때까지 기다리고, 작업의 결과를 반환할 수도 있다.

private void button37_Click(object sender, EventArgs e)
{
    TaskTest task = new TaskTest();
    task.DoMultipleTasksAsync();
}
public class TaskTest
{

    public async Task DoMultipleTasksAsync()
    {
        var tasks = new List<Task>();

        // Task.Run() 메소드를 사용하여 다른 스레드에서 실행되는 작업을 생성
        for (int i = 0; i < 5; i++)
        {
            tasks.Add(Task.Run(() =>
            {
                Debug.WriteLine($"{i}번째 작업을 완료했습니다.");
            }));
        }

        // 모든 작업이 완료될 때까지 기다림
        await Task.WhenAll(tasks);

        Console.WriteLine("모든 작업이 완료되었습니다.");
    }
}

// 실행 결과: 숫자는 매번 바뀜
// 2번째 작업을 완료했습니다.
// 2번째 작업을 완료했습니다.
// 4번째 작업을 완료했습니다.
// 4번째 작업을 완료했습니다.
// 5번째 작업을 완료했습니다.
profile
기록만이 살 길 ... 말하는 감자애오

0개의 댓글