자바스크립트

[JavaScript] async, await를 이용한 ajax 통신

h__hj 2022. 9. 10. 20:59

# async, await를 이용한 ajax 통신

 async와 await 테스트 코드를 이용하여 비동기로 처리되는 여러개의 ajax를 동기적으로 처리되도록 만들어 보기!

document 와 테스트 코드 블로그.

- async/await: Javascript Info
- async/await, test: 보러가기

해당 코드는 올바른 코드라고 생각하진 않음. 따라하지 마셈. 그냥 한번 적용해보고 싶어서 하는 거임.

# 환경

/**
 * tool: STS 4.13.0
 * version: 2.7.3-SNAPSHOT
 * java: 1.8
 * type: MAVEN
 * view: THYMELEAF
 * jQuery: 3.6.0
 */

# 페이지

# HTML

<h1>동기/비동기 테스트 페이지 입니다.</h1>

<div>
    <input id="toggle" type="button" onclick="toggle()" value="START" style="display: inline;"/>
    <br>
    <h3>SYNC/ASYNC</h3>
    <input type="button" onclick="callType(false)" value="SYNC" style="display: inline;"/>
    <input type="button" onclick="callType(true)" value="ASYNC" style="display: inline;"/>
    <br>
    <input type="button" onclick="callType(true, 'THEN')" value="ASYNC THEN" style="display: inline;"/>
    <input type="button" onclick="callType(false, 'THEN')" value="SYNC THEN" style="display: inline;"/>
    <br>
    <input type="button" onclick="callType(true, 'AWAIT')" value="ASYNC AWAIT" style="display: inline;"/>
    <input type="button" onclick="callType(false, 'AWAIT')" value="SYNC AWAIT" style="display: inline;"/>
    <br>
    <input type="button" onclick="callType(true, 'ALL')" value="ASYNC THEN ALL" style="display: inline;"/>
    <input type="button" onclick="callType(false, 'ALL')" value="SYNC THEN ALL" style="display: inline;"/>
    <br>
    <input type="button" onclick="callTest()" value="TEST CALL" style="display: inline;"/>
</div>

# ajax 호출 함수, sleep ajax 함수.

// ajax test 호출 함수.
function callType(isAsync, name) {
    const type = (isAsync) ? '비동기' : '동기';
    console.log(`${name}: START(${type}):`, timestamp());
		
    if(name === 'THEN') {
        callThen(isAsync);
			
    } else if(name === 'ALL') {
        callThenAll(isAsync);
	
    } else if(name === 'AWAIT') {
        callAwait(isAsync);
			
    } else {
        call(isAsync);
    }
		
    console.log(`${name}: STOP(${type}):`, timestamp());
}
// 실제 ajax 함수.
function promiseSleep(isAsync, ss) {
    return new Promise(function(resolve) { 
        const type = (isAsync) ? '비동기' : '동기';
        console.log(`${ss}SS: CALL(${type}):`, timestamp());
        $.ajax(`/test/async/sleep${ss}`,
            {
            // settings
                method  : "GET",
                async   : isAsync,
                data    : {
                    text: 'sleep on!'
                },
                dataType: "json", 
            }).done(function(output, textStatus, jqXHR) {
                console.log(`${ss}SS: BACK(${type}):`, timestamp());
            }).fail(function(jqXHR) {
	
            }).always(function(output, textStatus, jqXH) {
                console.log(`${ss}SS: COMPLETE(${type}):`, timestamp());
                resolve('ajax is complete!');
            }
        );
    });
}

# Controller

@Slf4j
@Controller
@RequestMapping("/test/async")
public class TestAsyncController {

    @GetMapping("/sleep2")
    public ModelAndView sleep2(@RequestParam Map<String, String> input) {
        // 2초 대기.
        TestAsyncController.delay(2000);
        ModelAndView mav = new ModelAndView("jsonView");
        mav.addObject("output", "sleep2 Ok!");
        return mav;
    }
    @GetMapping("/sleep3")
    public ModelAndView sleep3(@RequestParam Map<String, String> input) {
        // 3초 대기.
        TestAsyncController.delay(3000);
        ModelAndView mav = new ModelAndView("jsonView");
        mav.addObject("output", "sleep2 Ok!");
        return mav;
    }
    @GetMapping("/sleep4")
    public ModelAndView sleep(@RequestParam Map<String, String> input) {
        // 4초 대기.
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ModelAndView mav = new ModelAndView("jsonView");
        mav.addObject("output", "sleep4 Ok!");
        return mav;
    }
    @GetMapping("/test")
    public ModelAndView test(@RequestParam Map<String, String> input) {
        ModelAndView mav = new ModelAndView("jsonView");
        mav.addObject("output", "test Ok!");
        return mav;
    }
    // 어쩌다가 발견한 delay 함수
    public static boolean delay(int ms) {
        long now   = System.currentTimeMillis();
        long after = now + ms;
        while(after >= now) {
            now = System.currentTimeMillis();
        }
        return true;
    }
}

# 테스트 코드에서 TYPE1 과 같은...

async function callAwait(isAsync) {
    const message4 = await promiseSleep(isAsync, 4);
    console.log(timestamp(), ':', message4);

    const message2 = await promiseSleep(isAsync, 2);
    console.log(timestamp(), ':', message2);
}
/*  ASYNC
    ING: 17:28:41
    ING: 17:28:42
AWAIT: START(비동기): 17:28:42
    4SS: CALL(비동기): 17:28:42
AWAIT: STOP(비동기): 17:28:42
    ING: 17:28:43
    ING: 17:28:44
    ING: 17:28:45
    ING: 17:28:46
    4SS: BACK(비동기): 17:28:46
    4SS: COMPLETE(비동기): 17:28:46
    17:28:46 : ajax is complete!
    2SS: CALL(비동기): 17:28:46
    ING: 17:28:47
    ING: 17:28:48
    2SS: BACK(비동기): 17:28:48
    2SS: COMPLETE(비동기): 17:28:48
    17:28:48 : ajax is complete!
    ING: 17:28:49
    ING: 17:28:50
*/
  • 이전 블로그에서 작성한 테스트 코드에서 TYPE1 과 같은 진행을 구현하고 싶었다.
  • 함수는 비동기로 해당 함수호출 후 종료. (비동기로 진행중이라 스크립트는 계속 진행)
  • 4초 함수 종료 후, 2초 함수 실행.

# TYPE1 내용

  • 지연되는 시간이 있지만, 스크립트가 비동기적으로 움직이면서, 동기적으로 실행된다는 점에서 메리트가 있다.
  • 순서가 보장되어야 하는 연속 비동기 ajax에 적합 할 듯 하다.

# TYPE1 대신

  • 첫번째 ajax가 끝나고 done이나 complete 함수에서 또 다른 ajax를 실행해도 됨.(트리 모양으로 ajax가 진행됨.)

# 테스트 코드에서 TYPE2, TYPE3과 같은...

function callTest() {
    const isAsync = true;
    Promise.all([promiseSleep(isAsync, 4), promiseSleep(isAsync, 2), promiseSleep(isAsync, 3)]).then((messages) => {
        if(messages[0] && messages[1] && messages[2]) {
            console.log('3개의 ajax 완료!');
        }
        callTestAjax();
    });
}
	
function callTestAjax() {
    $.ajax(`/test/async/test`,
        {
        // settings
            method  : "GET",
            async   : true,
            data    : {text: 'sleep on!'},
            dataType: "json", 
        }).done(function(output, textStatus, jqXHR) {
            console.log("OUTPUT:", output);
        }).fail(function(jqXHR) {
				
        }).always(function(output, textStatus, jqXH) {
    });
}
/*
    ING: 21:34:55
    ING: 21:34:56
    4SS: CALL(비동기): 21:34:56
    2SS: CALL(비동기): 21:34:56
    3SS: CALL(비동기): 21:34:56
    ING: 21:34:57
    ING: 21:34:58
    2SS: BACK(비동기): 21:34:58
    2SS: COMPLETE(비동기): 21:34:58
    ING: 21:34:59
    3SS: BACK(비동기): 21:34:59
    3SS: COMPLETE(비동기): 21:34:59
    ING: 21:35:00
    4SS: BACK(비동기): 21:35:00
    4SS: COMPLETE(비동기): 21:35:00
    3개의 ajax 완료!
    OUTPUT: {output: 'test Ok!'}
    ING: 21:35:01
    ING: 21:35:02
*/
  • 여러개의 ajax 가 동시에 비동기로 처리되고, 함수는 종료된다.
  • ajax가 다르게 끝나더라고 마지막에 종료되는 ajax를 await 해준다.
  • 다 종료가 되면 실제로 실행되어야 하는 ajax(callTestAjax)를 실행.
  • 앞에 2초지연, 3초지연, 4초지연 ajax는 callTestAjax를 실행하기 위한 사전 작업 같은 느낌의 통신으로 볼수있다!
  • 앞선 ajax가 완료 된 후, 최종 ajax의 실행이 보장되는 프로세스에 적합 할 듯 하다.

# TYPE2, TYPE3 내용

  • 예를 들어 3개의 ajax는 고객정보를 가져오고, 특정 DB의 데이터를 가져오고, 고객이 입력한 내용을 업데이트를 한 뒤에 시퀀스를 가져옴.
  • 3개의 데이터를 조합한 데이터를 "DB에 적재" 또는 "특정 제휴사에게 전달" 해야 된다면,
  • 비동기로 동시에 진행 시킨 이후에 await 한 후 진행하면 조금 더 빠른 프로세스를 만들수 있지 않을까 싶음!
  • 간편하게 하려면 해당 데이터를 동기적으로 구현해야 하며, 4+3+2=9초로 한번하는데 9초를 기다려야한다.
  • 그 와중에 동기여서 다른 스크립트는 멈춘 상태!

# TYPE2, TYPE3 대신,,,,

let is2Done = false;
let is3Done = false;
let is4Done = false;

function isDone() {
    if(is2Done && is3Done && is4Done) {
        // 실행되어야 하는 ajax....
    }
}
  • 각각의 함수종료를 나타내는 변수를 만들어주고, done()에서 true로 변경 후, isDone() 실행해도 됨.

# 내용

 async/await을 다루고 싶었고, ajax에 적용해서 사용해보고 싶었다.

 

# JavaScript 시리즈

https://hjho95.tistory.com/21 async, await
https://hjho95.tistory.com/22 async, await를 이용한 ajax

# JavaScript 시리즈의 참조 페이지

async/await: https://ko.javascript.info/async-await