코어자바스크립트(콜백)

L·2022년 11월 20일
0

코어자바스크립트

목록 보기
4/6
post-thumbnail

1.콜백 함수란?

다른 코드의 인자로 넘겨주는 함수.

콜백함수를 넘겨받은 코드는 적절한 시점에 실행.

콜백(callback)의 뜻은 '되돌아 호출해달라'는 뜻.

  • 함수를 호출시 매개변수를 함수로 받고 특정조건 떄 인자로받은 함수를 실행해서 알려달라고 요청을 보낸다.
  • 이때, 콜백 함수는 실행한 함수에 제어권을 위임. 즉, 위임받은 코드는 내부로직에 따라 콜백함수를 적절한 시점에 실행

2.제어권

1) 호출시점

var callback = function(){
  console.log('이건 setInterval이 1초마다 호출하는 함수입니다.');
}
setInterval(callback,1000);

setInterval함수는 인자로 받은 시간마다 callback함수를 호출하는데, 여기서 callback함수를 1000ms(1초) 사용자가 직접 실행하지않고, setInterval이 1초마다 알아서 실행을 시켜줌. 즉, callback 함수 실행에 대한 권한을 setInterval함수가 위임받아서 알아서 실행 시킨다고 보면 됨.

2)인자

const newArr = [];

[10,20,30].forEach(function(value,index,arr){
	newArr.push([ value, index, this[index],arr ]);
},[100,200,300]);

  console.log(newArr); //[[10,0,100,[10,20,30]],[20,1,200,[10,20,30]],[30,2,300,[10,20,30]]]

[10,20,30] 배열에 forEach 메서드를 실행. 첫 번째는 forEach 메서드가 호출 할 callback함수, 두번째는 forEach 메서드안에서 this로 인식할 대상인 thisArg가 들어감(생략가능).

arr.map(callback(currentValue[, index[, array]])[, thisArg])

  • forEach 메서드는 요소를 하나하나씩 꺼내어 콜백함수를 실행. 콜백함수의 내용은 newArr 배열에 배열의 요소,배열의 위치값, 그리고 this로 인식한 대상 배열의 위치 값을 담은 배열을 newArr에 넣는 일을 수행.
const newArr = [];

[10,20,30].forEach(function(index,value){
	newArr.push([ value, index, this[index] ]);
},[100,200,300]);

  console.log(newArr); //[[0,10,undefined,[10,20,30]],[1,20,undefined,[10,20,30]],[2,30,undefined,[10,20,30]]]
  • 만약 forEach메서드를 jQueryeach메서드처럼 썻다고 가정하에 작성.( each메서드의 콜백함수에는 첫 번째는 index와 두 번째는 value가 옴)
  • newArr 배열의 값을 콘솔로 출력하면, value는 값의 위치, index는 값 ,3번째는 thisArg 배열의 index 위치의 값인 undefined가 들어가 있음. 사용자는 jQueryeach처럼 분명 순서를 바꿔서 사용하겠다고 작성했지만, 이름은 사용자가 명명할것일뿐, 해당 값은 사용자가 매개변수 명을 다르게 변경을 해도 성격은 똑같음. 즉, forEach메서드에서 인자로 받는 콜백함수는 forEach함수가 제어권을 가지고있어서, 원하는 대로 배열이 돌아가기위해서는 메서드의 규칙을 따라야 함.

3) this

  • 이전에 this 에서 작성했듯 콜백함수에서의 this는 기본적으로 전역 객체를 참조하지만, 제어권을 넘겨받은 코드에 별도로 지정된 경우에는 전역 객체가 아닐수가 있음.
Array.prototype.forEach = function(callback,thisArg){
	for(var i=0;i<this.length;i++){
      callback.call(thisArg || window, this[i],i,this);
    }
}

위에 소스를 보면 콜백함수를 호출 할 때 call을 붙여서 호출하는데 콜백함수 내부에서 thisthisArg에 값이 있으면 thisArg이고 없으면 전역 객체를 가리킴. 그래서 콜백함수의 this도 제어권을 넘겨받은 코드에서 callapply 메서드를 통해서 this의 값도 명시적으로 바인딩 됨.

setTimeout(function(){console.log(this);},1000); // window

document.body.innerHTML='<button id="a">클릭</button>';
document.querySelector('#a').addEventListener('click',function(e){
console.log(this,e); // <button id="a">클릭</button>, MouseEvent{...}
});
  • setTimeout에서 인자로 받은 콜백함수안의 this는 값을 바인딩하지않고 그대로 호출했기때문에 전역객체를 가리킴.
  • addEventListener에서 인자로 받은 콜백함수안의 this는 내부에서 콜백함수를 호출할때 this값을HTML요소값을 바인딩하여 호출했기때문에 로그에는 button 요소를 가리킴.
    만약 콜백함수에 array function문법을 이용했다면, this 값을 바인딩하는 과정이 제외 되기 때문에 스코프 체인상 가까운 전역 객체를 가리킴.

3. 콜백 함수는 함수다.

var obj = {
  array:[1,2,3],
  logValues:function(v,i){
    console.log(v,i,this);
  }
}
obj.logValues(1,2);//1,2,{array:[1,2,3],logValues:f}
[4,5,6].forEach(obj.logValues);//[4,0,Window][5,1,Window],[6,2,Window]
  • obj객체안의 logValues를 호출시 thisobj객체를 가리키는것은 당연.
  • 하지만, forEach의 콜백함수를 obj.logValues를 전달하는 순간 obj 객체와는 연간성이 사라져, 배열을 반복할때마다, this의 값은 전역 객체를 가리키게 됨.

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

  1. 전달하고자하는 this객체를 다른 변수에 담아서 콜백함수에서 호출시 다른 변수 값으로 사용
  2. bind를 이용한 this객체에 다른값 바인딩.
var obj1 = {
  name:'obj1',
  func:function(){
      console.log(this.name);
  }
}
setTimeout(obj1.func.bind(obj1),1000); //obj1
var obj2 = {name:'obj2'};
setTimeout(obj1.func.bind(obj2),1000); //obj2

5. 콜백 지옥과 비동기 제어

콜백 지옥이란?
콜백 함수를 익명함수로 전달되는 과정이 계속 반복되어 들여쓰는 코드의 수준이 감당이 힘들정도로 깊어지는 현상.

해결방법 1. 기명함수로 변환

var name = '';
var callback=function(param){
  name = param;
  console.log(name);
  setTimeout(callback2,1000,'obj2');
}
var callback2=function(param){
  name = param;
  console.log(name);
  setTimeout(callback3,1000,'obj3');
}
var callback3=function(param){
  name = param;
  console.log(name);
  setTimeout(callback4,1000,'obj4');
}
var callback4=function(param){
  name = param;
  console.log(name);
}
setTimeout(callback,1000,'obj1');

해결방법 2.Promise

new Promise(function(resolve,reject){
  setTimeout(function(){
    var name = 'obj1';
    console.log(name);
    resolve(name);
  },1000);
}).then(function(prevName){
  return new Promise(function(resolve,reject){
      setTimeout(function(){
        var name = `${prevName} obj2`;
        console.log(name);
        resolve(name);
      },1000);
	})
}).then(function(prevName){
	return new Promise(function(resolve,reject){
      setTimeout(function(){
        var name = `${prevName} obj3`;
        console.log(name);
        resolve(name);
      },1000);
	})
})

Promise 인스턴스에서 인자로 넘겨주는 콜백함수안의 비동기 처리 후 resolve를 호출하면 then(다음) 메소드를 호출하면, 프로미스 객체를 반환하기 때문에, 이어서 비동기 방식을 호출 할 수 있어 들여쓰는 코드를 줄일 수 있음.(메소드 체이닝 기법)

해결방법 3.Promise + async/await

var func = function(name){
  return new Promise(function(resolve,reject){
    setTimeout(function(){
      resolve(name)
    },1000)
  });
};

var callFunc = async function(){
  var names = '';
  var call1 = async function(name){
    names = `${names} ${await func(name)}`;
  }
  await call1('obj1');
  console.log(names);
  await call1('obj2');
  console.log(names);
  await call1('obj3');
  console.log(names);
  await call1('obj4');
  console.log(names);
  
}
callFunc();

async/await는 비동기를 마치 동기방식처럼 사용할수있는 방식이며, await안에 비동기처리가 끝날때까지 아래에 소스는 작동하지 않음. 비동기 방식을 수행하고자 하는 함수 앞에 async를 넣고, 함수 내에서 비동기를 작업하는 위치에 await를 넣으면 됨.(단, await가 들어가는 함수는 Promise객체를 리턴해야함)

정리

  • 콜백함수란 함수 호출시 매개변수에 함수를 인자로 넘겨줌으로써 그 제어권을 호출하는 함수에게 위임.
  • 위임받은 함수가 가지는 제어권
    1 .호출 시점
    2 .콜백 함수호출 시 넘겨주는 인자 값과 순서
    3 . this(기본적으로 전역객체를 바라보지만, 임의로 바꾸고 싶은 경우는 bind메서드를 통해 임의로 할당.
  • 콜백함수를 익명함수로 전달되는 과정이 반복되어 코드의 수준이 감당안아될정도로 깊어지는 현상을 콜백함수라고함. 이를 해결하기 위해서 Promise, async/await 등 여러가지 방법들이 나오고 있음.

참고

0개의 댓글