[Javascript] Closure

closure는 주변 state(lexical environment를 의미)에 대한 참조와 함께 묶인 함수의 조합이다. 다시말해서, closure는 inner function이 outer function의 scope를 접근할 수 있게 해준다. JavaScript에서 closure는 함수 생성 시간에 함수가 생성될 때마다 만들어진다.

Lexical scoping

아래 예제를 보자

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // use variable declared in the parent function
  }
  displayName();
}
init();

closure는 inner function이 outer function의 scope에 접근할 수 있기 때문에 위의 예제에서 inner function인 displayName()이 outer function인 init()의 local 변수 name을 참조하고 있다.

lexical scoping은 nested 함수에서 변수 이름이 확인되는 방식을 정의한다. inner function은 parent function이 return 되었더라고 parent function의 scope를 가지고 있다. 아래 예제를 보자

/* lexical scope (also called static scope)*/
function func() {
    var x = 5;
    function func2() {
        console.log(x);
    }
    func2();
}

func() // print 5
/* dynamic scope */
function func() {
    console.log(x)
}

function dummy1() {
    x = 5;
    func();
}

function dummy2() {
    x = 10;
    func();
}

dummy1() // print 5
dummy2() // print 10

첫 번째 예제는 compile-time에 추론할 수 있기 때문에 static이며 두 번째 예제는 outer scope가 dynamic 하고 function의 chain call에 의존하기 때문에 dynamic이라고 불린다.

Closure

function makeFunc() {
  var name = 'Mozilla';
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

위의 예제는 처음의 init() 함수와 같은 효과를 가진다. 차이점은 inner function인 displayName()이 outer function이 실행되기 이전에 return 되었다는 것이다.

다른 programming language에서는 함수의 local variable은 함수가 실행되는 동안에서만 존재한다. makeFunc()가 호출되고 끝난다음에 더 이상 name 변수에 접근하지 못해야 할 것 같지만 JavaScript에서는 그렇지 않다.

그 이유는 JavaScript의 함수가 closure를 형성하기 때문이다. closure란 함수와 lexical environment의 조합이다. 이 environment는 closure가 생설 될 때 scope 내에 있던 모든 local 변수로 구성된다. 위의 경우에, myFunc는 makeFunc가 실행될 때 만들어진 displayName의 instance를 참조한다. displayName의 instance는 name 변수를 가진 lexical environment를 참조하는 것을 유지한다. 이러현 이유로 myFunc가 실행 될 때, name 변수는 사용가능한 상태로 남아있다.

closure는 매우 유용하다. 왜냐하면 data와 함수를 연결 시켜주기 때문이다. 이것은 data와 하나 또는 여러개의 method와 연결 되어있는 OOP(object-oriented programming)과 똑같다.

결국 closure를 이용하여 OOP의 object로 이용할 수 있다.

Emulating private methods with closures

Java와 다르게 JavaScript은 private를 구현하기 위한 native 방법을 제공하지 않는다. 그러나 closure를 통해서 private를 구현할 수 있다.

아래 예제는 Module Design Pattern을 따른다.

var counter = (function() {
  var privateCounter = 0;

  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment: function() {
      changeBy(1);
    },

    decrement: function() {
      changeBy(-1);
    },

    value: function() {
      return privateCounter;
    }
  };
})();

console.log(counter.value());  // 0.

counter.increment();
counter.increment();
console.log(counter.value());  // 2.

counter.decrement();
console.log(counter.value());  // 1.

위의 예제에서 counter.increment 와 counter.decrement, counter.value는 같은 lexical environment를 공유하고 있다.

공유된 lexical environment는 선언가 동시에 실행되는 anonymous function(IIFE)의 body에 생성되어 있다. lexical environment는 private 변수와 함수를 가지고 있어 anonymous function의 외부에서 접근할 수 없다.

아래는 anonymous function이 아닌 function을 사용한 예제이다

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },

    decrement: function() {
      changeBy(-1);
    },

    value: function() {
      return privateCounter;
    }
  }
};

var counter1 = makeCounter();
var counter2 = makeCounter();

alert(counter1.value());  // 0.

counter1.increment();
counter1.increment();
alert(counter1.value()); // 2.

counter1.decrement();
alert(counter1.value()); // 1.
alert(counter2.value()); // 0.

위의 예제는 closure 보다는 object를 사용하는 것을 추천한다. 위에서 makeCounter() 가 호출될 때마다 increment, decrement, value 함수들이 새로 assign되어 오버헤드가 발생한다. 즉, object의 prototype에 함수들을 선언하고 object를 운용하는 것이 더 효율적이다.

function makeCounter() {
    this.publicCounter = 0;
}

makeCounter.prototype = {
    changeBy : function(val) {
        this.publicCounter += val;
    },
    increment : function() {
        this.changeBy(1);
    },
    decrement : function() {
        this.changeBy(-1);
    },
    value : function() {
        return this.publicCounter;
    }
}
var counter1 = new makeCounter();
var counter2 = new makeCounter();

alert(counter1.value());  // 0.

counter1.increment();
counter1.increment();
alert(counter1.value()); // 2.

counter1.decrement();
alert(counter1.value()); // 1.
alert(counter2.value()); // 0.

Closure Scope Chain

모든 closure는 3가지 scope를 가지고 있다.

  • Local Scope(Own scope)
  • Outer Functions Scope
  • Global Scope
// global scope
var e = 10;
function sum(a){
  return function(b){
    return function(c){
      // outer functions scope
      return function(d){
        // local scope
        return a + b + c + d + e;
      }
    }
  }
}

console.log(sum(1)(2)(3)(4)); // log 20

// You can also write without anonymous functions:

// global scope
var e = 10;
function sum(a){
  return function sum2(b){
    return function sum3(c){
      // outer functions scope
      return function sum4(d){
        // local scope
        return a + b + c + d + e;
      }
    }
  }
}

var s = sum(1);
var s1 = s(2);
var s2 = s1(3);
var s3 = s2(4);
console.log(s3) //log 20

위의 예제를 통해서 closure는 모든 outer function scope를 가진다는 것을 알 수 있다.

Creating closures in loops: A common mistake

아래 예제를 보자

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
  document.getElementById('help').textContent = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

위의 코드는 정상적으로 동작하지 않는다. 모든 element에서 age의 help text가 보일 것이다. 그 이유는 onfocus가 closure이기 때문이다. closure는 function 선언과 setupHelp의 fucntion scope를 가지고 있다. 3개의 closure를 loop에 의해서 만들어지며 같은 lexical environment를 공유하고 있다. 하지만 item은 var로 선언이 되어있어 hoisting이 일어난다. item.help는 onfocus 함수가 실행될 때 결정되므로 항상 age의 help text가 전달이 된다. 아래는 해결방법이다.

function showHelp(help) {
  document.getElementById('help').textContent = help;
}

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();

하나의 lexical environment를 공유하는 대신 makeHekpCallback 함수가 새로운 lexical environment를 만들었다.

다른 방법으로는 anonymous closure(IIFE)를 이용한다.

function showHelp(help) {
  document.getElementById('help').textContent = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    (function() {
       var item = helpText[i];
       document.getElementById(item.id).onfocus = function() {
         showHelp(item.help);
       }
    })(); // Immediate event listener attachment with the current value of item (preserved until iteration).
  }
}

setupHelp();

let keyword를 사용해서 해결할 수 있다.

function showHelp(help) {
  document.getElementById('help').textContent = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (let i = 0; i < helpText.length; i++) {
    let item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

Performane consideration

closure가 필요하지 않을 때 closure를 만드는 것은 메모리와 속도에 악영향을 끼치낟.

예를들어, 새로운 object/class를 만들 때, method는 object의 생성자 대신에 object의 prototype에 있는 것이 좋다. 왜냐하면 생성자가 호출될 때마다, method는 reassign 되기 때문이다.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}

위의 예제에서 getName과 getMessage는 생성자가 호출될 때마다 reaasign된다.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};

prototype 전부를 다시 재선언하는 것은 추천하지 않는다.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

'Language > Javascript' 카테고리의 다른 글

[Javasript] Object Prototype  (0) 2022.05.08
[Javascript] ES2015+ 요약 정리  (0) 2022.05.08

Object Prototype

Prototype은 JavaScript object가 다른 object에서 상속하는 매커니즘이다.

A prototype-based language?

JavaScript는 종종 prototype-based language로 설명된다. prototype-based language는 상속을 지원하고 object는 prototype object를 갖는다. prototyp object는 method와 property를 상속하는 template object 같은 것이다.

object의 prototype object 또한 prototype object를 가지고 있으며 이것을 prototype chain 이라고 부른다.

JavaScript에서 연결은 object instance와 prototype(_proto_ 속성 또는 constructor의 prototype 속성) 사이에 만들어진다

Understanding prototype objects

아래 예제를 보자.

function Person(first, last, age, gender, interests) {

  // property and method definitions
  this.name = {
    'first': first,
    'last' : last
  };
  this.age = age;
  this.gender = gender;
  //...see link in summary above for full definition
}

우리는 object instace를 아래와 같이 만들 수 있다.

let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);

person1에 있는 method를 부른다면 어떤일이 발생할 것인가?

person1.valueOf()

valueOf()를 호출하면

  • 브라우저는 person1 object가 valueOf() method를 가졌는지 확인한다. 즉, 생성자인 Person()에 정의되어 있는지 확인한다.
  • 그렇지 않다면 person1의 prototype object를 확인한다. prototype object에 method가 없다면 prototype object의 prototype object를 확인하며 prototype object가 null이 될 때까지 탐색한다.

'Language > Javascript' 카테고리의 다른 글

[Javascript] Closure  (0) 2022.05.08
[Javascript] ES2015+ 요약 정리  (0) 2022.05.08

[Javascript] ES2015+ 요약 정리



ES2015+의 등장

기존의 자바스크립트 문법에 다른 언어의 장점들을 더한 편리한 기능들이 많이 추가되었다. 이 중에 활용도가 높은 부분에 대해서 알아보자.

 

1. const, let


자바스크립트에서 변수를 선언할 때 var를 이용해왔다. 하지만 이제 var는 const와 let으로 대체할 것이다.

const와 let은 함수 스코프를 가지는 var와는 달리 블록 스코프를 갖는다.

블록 스코프는 if, while, for, function 등에서 사용하는 중괄호에 속하는 범위를 뜻한다. 따라서 const와 let을 이 중괄호 안에서 사용하게 된다면, 그 스코프 범위 안에서만 접근이 가능하다. 이를 통해 호이스팅에 관련된 문제는 자연스럽게 해결할 수 있다.

 

그렇다면 const와 let은 무슨 차이일까?

간단히 말해서 let은 대입한 값을 계속 수정할 수 있지만, const는 한번 대입하면 다른 값 대입을 할 수 없고 초기화 시 값이 필요다.

const a = 0;
a = 1; // error


let b = 0;
b = 1; // 1

const c; // error 



2. 템플릿 문자열


백틱(`)을 이용해 새로운 문자열을 만들 수 있다.

백틱은 키보드에서 tab키 위에 있다

 

백틱을 활용해서 문자열 안에 변수도 넣을 수 있게 되었다. 기존에는 변수가 등장할 때마다 따옴표를 닫고 +를 통해 연결했는데 이제 백틱을 활용하면 변수가 포함된 문자열을 한번에 모두 작성이 가능해졌다.

 

var string = num1 + ' + ' + num2 + ' = ' + result;

const string = `${num1} + ${num2} = ${result}`;

 

아래가 훨씬 가독성이 좋아졌다. 또한, 백틱 안에 따옴표를 함께 작성하는 것도 가능하다.



3. 객체 리터럴


다음 코드는 oldObject 객체에 동적으로 속성을 추가하는 상황이다.

  • 기존 코드
var sayNode = function() {
    console.log('Node');
};
 
var es = 'ES';
var oldObject = {
    sayJS: function(){
        console.log('JS');
    },
    sayNode: sayNode,
};
 
oldObject[es + 6] = 'Fantastic';
 
oldObject.sayNode();
oldObject.sayJS();
console.log(oldObject.ES6);

 

이제 위와 같은 코드를 아래처럼 수정할 수 있다.

var sayNode = function() {
    console.log('Node');
};
 
var es = 'ES';
 
const newObject = {
    sayJS() {
        console.log('JS');
    },
    sayNode,
    [es+6]: 'Fantastic',
};
 
newObject.sayNode();
newObject.sayJS();
console.log(newObject.ES6);

 

oldObject와 newObject를 비교해보자.

객체의 메서드에 함수를 연결할 때 이제 :와 같은 콜론과 function을 붙이지 않아도 가능해졌다.

또한 sayNode : sayNode와 같이 중복되는 이름의 변수는 그냥 간단히 sayNode 하나만 작성하면 된다.

또한 객체의 속성명을 동적으로 생성이 가능하다. 이전에는 객체 리터럴 바깥에서 [es+6]으로 만들었지만, 이제 객체 리터럴 안에서 만들 수 있는 모습을 확인할 수 있다.

코드의 양을 줄이고, 편리하니 익숙해지면 좋다.



4. 화살표 함수


기존의 function {}도 이전처럼 사용이 가능하지만, ES2015 이후로 화살표 함수가 생기면서 많이 사용되고 있다.

function add1(x, y) {
	return x+y;
}

const add2 = (x, y) => x + y;

 

두 가지 모두 똑같은 기능을 하는 함수다. 하지만 화살표 함수에서는 function 대신 => 기호로 선언한다. 이는 return문을 줄일 수 있는 장점이 있다. 또한 화살표 함수는 function과 this 바인드 방식에서 차이점이 존재한다.

 

  • 기존 코드
var relationship1 = {
    name: 'kim',
    friends: ['a', 'b', 'c'],
    logFriends: function() {
        var that = this; // relationship1을 가리키는 this를 that에 저장
 
        this.friends.forEach(function(friend){
            console.log(that.name, friend);
        });
    },
};
relationship1.logFriends();

 

relationship1.logFriends()에서 forEach문 안에 function 선언문을 사용했다.

이로써 각자 다른 함수 스코프 this를 가지게 되므로 friends 값을 가져오기 위해서 that이라는 변수를 만들어 이에 this 값을 미리 저장해놓는 모습이다.

 

const relationship2 = {
    name: 'kim',
    friends: ['a', 'b', 'c'],
    logFriends() {
        this.friends.forEach(friend => {
            console.log(this.name, friend);
        });
    },
};
relationship2.logFriends();

 

이번에는 forEach문에서 function을 선언하지 않고 화살표 함수를 사용했다.

따라서 바로 바깥 스코프인 logFriends()의 this를 그대로 사용이 가능한 상황입니다. 이런 상황에서는 function 대신 화살표 함수를 사용하면서 따로 바깥 스코프의 this를 저장해놓고 불러오지 않아도 되서 편리하다.



5. 비구조화 할당


객체나 배열에서 속성 혹은 요소를 꺼내올때 사용한다.

 

  • 기존 코드
var candyMachine = {
    status: {
        name: 'node',
        count: 5,
    },
    getCandy: function(){
        return "Hi";
    }
};
 
var getCandy = candyMachine.getCandy;
var count = candyMachine.status.count;

기존에는 객체에서 속성을 가져올 때 이처럼 작성했다.

 

const candyMachine1 = {
    status: {
        name: 'node',
        count: 5,
    },
    getCandy1() {
        return "Hi";
    }
};
 
const { getCandy1, status: { count } } = candyMachine1;

console.log(getCandy1()) // Hi
console.log(count) // 5

하지만 이처럼 간단하게 한 줄로 나타내는 것이 가능해졌다. 여러 단계 안의 속성도 count1을 가져오는 것처럼 작성이 가능하다.

 

이 뿐만 아니라, 배열에서도 마찬가지로 적용이 가능하다.

var array = ['nodejs', {}, 10, true];
var node = array[0];
var obj = array[1];
var bool = array[array.length - 1];

 

array라는 배열 안에 4가지 요소를 넣고 가져오는 모습을 확인할 수 있다.

 

const array1 = ['nodejs', {}, 10, true];
const [node, obj, , bool] = array1;

 

bool은 true를 가져오기 위해 배열의 마지막 부분에 작성한 걸 볼 수 있다. 이처럼 작성하면 맨 끝이라고 자동으로 인식해주니 상당히 편한 장점이 있다.

 

이처럼 비구조화 할당을 이용하면, 배열이 위치마다 변수를 넣어 똑같은 역할을 하도록 만들 수 있다. 코드 줄도 상당히 줄일 수 있고, 특히 Node.js에서는 모듈을 사용하기 때문에 이런 방식이 많이 사용된다고 한다.



6. 프로미스(promise)


자바스크립트와 Node는 비동기 프로그래밍으로 이벤트 주도 방식을 활용하면서 콜백 함수를 많이 사용하게 된다. 콜백 함수 자체가 복잡한 것도 있고, 이해하기 어려운 자바스크립트 내용 중 하나이기도 하다.

 

이에 ES2015부터는 콜백 대신 API들이 프로미스 기반으로 재구성되고 있다. 따라서 프로미스에 대해 잘 이해하고 사용하게 된다면, 복잡한 콜백 함수의 지옥에서 벗어날 수 있으니 확실히 알고 있어야 한다.

 

promise 객체 구조는 아래와 같다.

const condition = true;
 
const promise = new Promise((resolve, reject) => {
    if (condition){
        resolve('성공');
    } else {
        reject('실패');
    }
});
 
promise
    .then((message) => {
        console.log(message);
    })
    .catch((error) => {
        console.log(error);
    });

 

new Promise로 프로미스를 생성할 수 있다. 그리고 안에 resolve와 reject를 매개변수로 갖는 콜백 함수를 넣는 방식이다.

이제 선언한 promise 변수에 then과 catch 메서드를 붙이는 것이 가능하다.

resolve가 호출되면 then이 실행되고, reject가 호출되면 catch가 실행된다.

이제 resolve와 reject에 넣어준 인자는 각각 then과 catch의 매개변수에서 받을 수 있게 되었다.

즉, condition이 true가 되면 resolve('성공')이 호출되어 message에 '성공'이 들어가 log로 출력된다. 반대로 false면 reject('실패')가 호출되어 catch문이 실행되고 error에 '실패'가 되어 출력될 것이다.

 

이제 이러한 방식을 활용해 콜백을 프로미스로 바꿔보자.

function findAndSaveUser(Users) {
    Users.findOne({}, (err, user) => { // 첫번째 콜백
        if(err) {
            return console.error(err);
        }
        user.name = 'kim';
        user.save((err) => { // 두번째 콜백
            if(err) {
                return console.error(err);
            }
            Users.findOne({gender: 'm'}, (err, user) => { // 세번째 콜백
                // 생략
            });
        });
    });
}

 

보통 콜백 함수를 사용하는 패턴은 이와 같이 작성할 것이다. 현재 콜백 함수가 세 번 중첩된 모습을 볼 수 있다.

즉, 콜백 함수가 나올때 마다 코드가 깊어지고 각 콜백 함수마다 에러도 따로 처리해주고 있다.

 

프로미스를 활용하면 아래와 같이 작성이 가능하다.

function findAndSaveUser1(Users) {
    Users.findOne({})
        .then((user) => {
            user.name = 'kim';
            return user.save();
        })
        .then((user) => {
            return Users.findOne({gender: 'm'});
        })
        .then((user) => {
            // 생략
        })
        .catch(err => {
            console.error(err);
        });
}

 

then을 활용해 코드가 깊어지지 않도록 만들었다. 이때, then 메서드들은 순차적으로 실행된다.

에러는 마지막 catch를 통해 한번에 처리가 가능하다. 하지만 모든 콜백 함수를 이처럼 고칠 수 있는 건 아니고, find와 save 메서드가 프로미스 방식을 지원하기 때문에 가능한 상황이다.

지원하지 않는 콜백 함수는 util.promisify를 통해 가능하다.

 

프로미스 여러개를 한꺼번에 실행할 수 있는 방법은 Promise.all을 활용하면 된다.

const promise1 = Promise.resolve('성공1');
const promise2 = Promise.resolve('성공2');
 
Promise.all([promise1, promise2])
    .then((result) => {
        console.log(result);
    })
    .catch((error) => {
        console.error(err);
    });

 

promise.all에 해당하는 모든 프로미스가 resolve 상태여야 then으로 넘어간다. 만약 하나라도 reject가 있다면, catch문으로 넘어간다.

기존의 콜백을 활용했다면, 여러번 중첩해서 구현했어야하지만 프로미스를 사용하면 이처럼 깔끔하게 만들 수 있다.



7. async/await


ES2017에 추가된 최신 기능이며, Node에서는 7,6버전부터 지원하는 기능이다. Node처럼 비동기 프로그래밍을 할 때 유용하게 사용되고, 콜백의 복잡성을 해결하기 위한 프로미스를 조금 더 깔끔하게 만들어주는 도움을 준다.

 

이전에 학습한 프로미스 코드를 가져와보자.

function findAndSaveUser1(Users) {
    Users.findOne({})
        .then((user) => {
            user.name = 'kim';
            return user.save();
        })
        .then((user) => {
            return Users.findOne({gender: 'm'});
        })
        .then((user) => {
            // 생략
        })
        .catch(err => {
            console.error(err);
        });
}

 

콜백의 깊이 문제를 해결하기는 했지만, 여전히 코드가 길긴 하다. 여기에 async/await 문법을 사용하면 아래와 같이 바꿀 수 있다.

 

async function findAndSaveUser(Users) {
    try{
        let user = await Users.findOne({});
        user.name = 'kim';
        user = await user.save();
        user = await Users.findOne({gender: 'm'});
        // 생략
 
    } catch(err) {
        console.error(err);
    } 
}

 

상당히 짧아진 모습을 볼 수 있다.

function 앞에 async을 붙여주고, 프로미스 앞에 await을 붙여주면 된다. await을 붙인 프로미스가 resolve될 때까지 기다린 후 다음 로직으로 넘어가는 방식이다.

 

앞에서 배운 화살표 함수로 나타냈을 때 async/await을 사용하면 아래와 같다.

const findAndSaveUser = async (Users) => {
    try{
        let user = await Users.findOne({});
        user.name = 'kim';
        user = await user.save();
        user = await user.findOne({gender: 'm'});
    } catch(err){
        console.error(err);
    }
}

 

화살표 함수를 사용하면서도 async/await으로 비교적 간단히 코드를 작성할 수 있다.

예전에는 중첩된 콜백함수를 활용한 구현이 당연시 되었지만, 이제 그런 상황에 async/await을 적극 활용해 작성하는 연습을 해보면 좋을 것이다.



[참고 자료]

'Language > Javascript' 카테고리의 다른 글

[Javascript] Closure  (0) 2022.05.08
[Javasript] Object Prototype  (0) 2022.05.08

+ Recent posts