개발 공부/JavaScript

콜백

무우너대갈 2022. 7. 15. 12:16
반응형

콜백 함수

  • 콜백 함수란?
    • 다른 코드(함수 || 메서드)의 인자로 넘겨주는 함수
    • 제어권도 함게 위임함

 제어권

  • 호출 시점
    • setInterval
      • scope : Window 객체 || Worker의 인스턴스
        • 브라우저 환경에서는 window를 생략해서 함수처럼 사용 가능
      • 세번째 매개변수
        • 선택적
        • func 함수를 실행할 때 매개변수로 전달할 인자
      • 어떠한 값도 리턴하지 않음
      • 고유한 ID값 반환 ⇒ clearInterval(중간 종료)를 위해
      var count = 0;
      var cbFunc = function() {
      	console.log(count);
      	if(++count > 4) clearInterval(timer);
      };
      var timer = setInterval(cbFunc, 300);
      
      //실행 결과
      //0 (0.3초)
      //1 (0.6초)
      //2 (0.9초)
      //3 (1.2초)
      //4 (1.5초)
      
      • timer 변수 : setInterval의 ID값 담김
      code 호출 주체 제어권
      cbFunc(); 사용자 사용자
      setInterval(cbFunc, 300); setInterval setInterval
    • var intervalID = scope.setInterval(func, delay[, param1, param2, ...]);
  • 인자
    • map
      콜백 함수의 실행 결과를 모아 새로운 배열 생성
      
      Array.prototype.map(callback [,thisArg])
      callback : function(currentalue, index, array)
      
      • 첫번째 인자 : callback 함수
      • 두번째 인자(생략 가능) : 콜백 함수 내부에서 this로 인식할 대상 특정
      • 콜백 함수의 인자
        • 첫번째 인자 : 배열의 요소 중 현재 값
        • 두번째 인자 : 현재값의 인덱스
        • map 메서드의 대상이 되는 배열 자체
      • 순서에 따라 값이 결정됨
        • 변수명을 바꿔도 값은 동일함
    • : 메서드의 대상이 되는 배열의 모든 요소들에 콜백 함수를 반복 호출
  • this
    • 콜백 함수도 함수 ⇒ 기본적으로 this가 전역객체 참조
    • 제어권을 넘겨받을 코드에서 별도로 this 지정 시, 그 대상 참조
    • map 메서드 구현
Array.prototype.map = function(callback, thisArg) {
	var mappedArr = [];
	for(var i = 0; i < this.length; i++){
		var mappedValue = callback.call(thisArg || window, this[i], i, this);
		mappedArr[i] = mappedValue;
	}
	return mappedArr;
};
  • this
    • thisArg 값이 존재하는 경우 해당 값 지정
    • 없을 경우 전역객체 지정
    • call/apply 메서드의 첫번째 인자
    • : 콜백 함수 내부에서 this가 될 대상을 명시적 바인딩
    setTimeout(function() {console.log(this);},300); // (1) Window{...}
    	// Timeout 객체가 나옴
    
    [1, 2, 3, 4, 5]. forEach(function(x) {
    		console.log(this); // (2) Window{...}
    });
    
    document.body.innerHTML +='<button id="a">클릭</button>';
    document.body.querySelector('#a')
    	.addEventListener('click', function(e) {
    		console.log(this, e); // (3) <button id ="a">믈랙</button>
    	} //MouseEvent {isTrusted : tru, ...}
    };
    
    

콜백 함수 == 함수

  • 메서드를 콜백 함수로 전달
    var obj = {
    	vals : [1, 2, 3],
    	logValues : function(v, i) {
    		console.log(this, v, i);
    	}
    };
    obj.logValues(1, 2); //{vals : [1, 2, 3], logValues : f} 1 2
    [4. 5. 6].forEach(obj.logValues); 
    //Window {...{} 4 0 
    //Window {...{} 5 1 
    //Window {...{} 6 2 
    
  • : 콜백 함수로 어떤 객체의 메서드를 전달하더라도 함수로서 호출

콜백 함수 내부의 this에 다른 값 바인딩하기

  • 전통적 방식
    • this를 다른 변수에 담아 콜백 함수로 활용할 함수에서 this 대신 변수를 사용
    • 이를 클로저로 사용
//콜백 함수 내부의 this에 다른 값을 바인딩
var obj1 = {
	name : 'obj1',
	func : function() {
		var self = this;
		return function() {
			console.log(self.name);
		};
	}
};
var callback = obj1.func();
setTimeout(callback, 1000);

//실제 this 사용 X + 번거로움
  • 콜백 함수 내부에서 this를 사용하지 않은 경우
//콜백 함수 내부에서 this 사용 X
var obj1 = {
	name : 'obj1',
	func : function() {
		console.log(obj1.name);
	}
};
setTimeout(obj.func, 1000);

//this 재활용 불가
//다른 객체를바라보게 못함 => 메모리 낭비
  • func 함수 재활용
var obj2 = {
	name : 'obj2',
	func : obj1.func
};
var callback2 = obj2.func();
setTimeout(callback2, 1500);

var obj3 = {name:'obj3'};
var callback3 = obj1.func.call(obj3);
setTimeout(callback3, 2000);
  • 콜백 함수 내부의 this에 다른 값 바인딩 - this
var obj1 = {
	name : 'obj1',
	func : function() {
		console.log(this.name);
	}
};
setTimeout(obj1.func.bind(obj1), 1000);

var obj2 = {name : 'obj2'};
setTimeout(obj.func.bind(obj2), 1500);
  1. 콜백 지옥과 비동기 제어
    • 콜백 지옥이란?
      • 콜백 함수를 익명 함수로 전달되는 과정 반복
      • ⇒ 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어짐
      • 비동기 작업을 수행에 자주 등장
        • 동기
          • 현재 실행 중인 코드가 완료된 후에 다음 코드 실행
          • CPU 계산에 의해 즉시 처리가 가능한 대부분의 코드
        • 비동기
          • 현재 실행 중인 코드의 완료 여부와 무관하게 다음 코드 실행
          • ex(별도의 요청, 실행 대기, 보류)
            • setTimeout
            • : 사용자의 요청에 의해 특정 시간이 경과되기 전까지 어떤 함수의 실행 보류
            • addEventListener
            • : 사용자의 직접적인 개입이 있을 때 함수를 실행하도록 대기
            • XMLHttpRequest
            • : 별도의 대상에 요청(웹브라우저 자체 X) 후 응답이 왔을 때 함수 실행
    • 콜백 지옥 해결

 

  • 기명 함수 전환
    • 코드의 가독성 높임
    • 함수 선언과 호출 구분 시 순서대로 읽어가는데 어려움 없음
    • 변수를 최상단으로 끌어올리며 외부에 노출 ⇒ 전체를 즉시 실행 함수 등 감싸 해결 가능
    var coffeeList = '';
    
    var addEspresso = function (name) {
    	coffeeList = name;
    	console.log(coffeeList);
    	setTimeout(addAmericano, 500, '아메리카노');
    };
    var addAmericano = function (name) {
    	coffeeList = name;
    	console.log(coffeeList);
    	setTimeout(addMocha, 500, '카페모카');
    };
    var addMocha= function (name) {
    	coffeeList = name;
    	console.log(coffeeList);
    	setTimeout(addLatte, 500, '카페라떼');
    };
    var addLatte= function (name) {
    	coffeeList = name;
    	console.log(coffeeList);
    };
    
    setTimeout(addEspresso, 500, '에스프레소');
    
    
  • Promise
    • ES6 도입
    • new 연산자와 함께 호출한 Promise의 인자로 넘겨주는 콜백 함수는 호출할 때 바로 실행
    • 내부의 resolve 또는 reject 함수를 호출하는 구문 존재
      • 둘 중 하나가 실행되기 전까지 다음(then) 또는 오류(catch) 실행 X
    new Promise(function (resolve) {
    	setTimeout(function () {
    		var name = '에스프레소';
    		console.log(name);
    		resolve(name);
    	}, 500);
    }).then(function (prevName) {
    	return new Promise(function (resolve) {
    		setTimeout(function () {
    			var name = prevName + ', 아메리카노';
    			console.log(name);
    			resolve(name);
    		},500);
    	});
    }).then(function (prevName) {
    	return new Promise(function (resolve) {
    		setTimeout(function() {
    			var name = prevNAme + ', 카페모카';
    			console.log(name);
    			resolve(name);
    		}, 500);
    	});
    }).then(function(prevName) {
    	return new Promise(function (resolve) {
    		setTimeout(function() {
    			var name = prevName + ' 카페라떼';
    			console.log(name);
    			resolve(name);
    		}, 500);
    	});
    });	
    
    
    //반복적 내용의 함수화
    var addCoffee = function(name) {
    	return function (prevName) { //클로저
    		return new Promise(function (resolve) { //클로저
    			setTImeout(function() {
    				var newName = prevName ? (prevName + ', ' + name) : name;
    				console.log(newName);
    				resolve(newName);
    			}, 500);
    		});
    	};
    };
    addCoffee('에스프레소')()
    	.then(addCoffee('아메리카노'))
    	.then(addCoffee('카페모카'))
    	.then(addCoffee('카페라떼'));
    
  • Generator
    • ES6 도입
    • Generator 함수 : * 붙은 함수
      • 실행 시 Iterator 반환
      • next 메서드 호출 시 Generator 함수 내부에 가장 먼저 등장하는 yield에서 함수 실행을 멈춤
      • 이후 next 메서드 호출 시 멈췄던 부분 시작 ⇒ 다음 yield에서 실행 멈춤
    var addCoffee = function(prevName, name) {
    	setTimeout(function () {
    		coffeeMaker.next(prevName ? prevName + ', ' + name : name);
    	},500);
    };
    var coffeeGenerator = function* () { //Generator
    	var espresso = yield addCoffe('', '에스프레소');
    	console.log(espresso);
    	var americano = yield addCoffe('', '아메리카노');
    	console.log(americano );
    	var mocha = yield addCoffe('', '카페모카');
    	console.log(mocha);
    	var latte = yield addCoffe('', '카페라떼');
    	console.log(latte);
    };
    var coffeeMaker = coffeeGenerator();
    coffeeMaker.next();
    
  • Promise + Async/await
    • ES2017 도입
    • async/await
      • aynce
      • : 비동기 작업을 수행하고자 하는 함수 앞에 표기
      • await ⇒ 뒤의 내용 Promise 자동 전환
      • ⇒ 해당 내용이 resolve 된 후에 다음으로 진행
      • : 함수 내부에서 실질적ㅇ니 비동기 작업이 필요한 위치마다 표기
    var addCoffee = function (name) {
    	return new Promise(function (resolve) {
    		setTimeout(function() {
    			resolve(name);
    		}, 500);
    	});
    };
    var coffeeMaker = async function() {
    	var coffeeList = '';
    	var _addCoffee = async function(name) {
    		coffeeList += (coffeeList ? ',' : '') + await addCoffee(name);
    	};
    	await _addCoffee('에스프레소');
    	console.log(coffeeList);
    	await _addCoffee('아메리카노');
    	console.log(coffeeList);
    	await _addCoffee('카페모카');
    	console.log(coffeeList);
    	await _addCoffee('카페라떼');
    	console.log(coffeeList);
    };
    coffeeMaker();
    

 

반응형

'개발 공부 > JavaScript' 카테고리의 다른 글

this 퀴즈  (0) 2022.07.17
클로저  (0) 2022.07.16
this  (0) 2022.07.14
실행 컨텍스트  (0) 2022.07.13
데이터 타입  (0) 2022.07.12