약속, 비동기/대기

약속하다

JavaScript와 Node에서는 주로 비동기를 만납니다. 특히 이벤트 리스너를 사용할 때 콜백 함수를 자주 사용합니다. 최근에는 JavaScript 및 Node API가 약속(콜백 대신)을 사용합니다.약속하다) 재계산 기반 콜백 bright(지옥을 회상하다) 현상을 극복한 것으로 평가된다. 따라서 Promise는 반드시 알아야 하는 객체입니다.

정당성

Promise는 지저분한 콜백 함수 코드에 대한 솔루션으로 비동기 JavaScript 처리에 사용되는 객체입니다. 여기서 자바스크립트 비동기 처리란 “특정 코드가 실행될 때까지 기다리지 않고 다음 코드를 먼저 실행하는 자바스크립트의 속성”을 말합니다. 한 단어로 설명실행되었지만 아직 결과를 반환하지 않은 객체‘.

Promise의 기본 코드를 보면서 설명을 이어가겠습니다.

const condition = true;  // true 면 resolve, false 면 reject
const promise = new Promise((resolve, reject) => {
  if (condition) {
    resolve('성공');
  } else {
    reject('실패');
  }
});

// 다른 코드가 들어갈 수 있음

promise
  .then((message) => {
    console.log(message);  // 성공(resolve) 한 경우 실행
  })
  .catch((error) => {
    console.error(error);  // 실패(reject) 한 경우 실행
  })
  .finally(() => {  // 끝나고 무조건 실행
    console.log('무조건');
});

새 약속으로 약속을 만들고 해결 및 거부 매개변수가 있는 콜백 함수를 삽입할 수 있습니다. 그런 다음 then 및 catch 메서드를 이 방식으로 생성된 약속 변수에 연결할 수 있습니다. 약속 속의 해결책 그게 언제를 의미합니까? 실행, 거부 부를 때 잡기 실행 중입니다.

결정하다 그리고 그러면 주어진 각 인수가 거부됩니다. 그리고 잡아 즉, resolve(‘success’)가 호출되면 매개변수에서 얻을 수 있습니다. 의 메시지는 “성공”입니다. “reject(‘failure’)”가 호출될 때 catch 오류는 “실패”입니다. 조건 변수를 false로 변경하여 시작 오류가 기록됩니다

간단히 말해서 약속은 즉시 실행되지만 나중에 결과를 받는 개체입니다. 결과 값은 실행이 완료된 후 반환됩니다. 또는 잡기 방법으로 얻습니다. 위의 예에서 new Promise와 Promise.then 사이에 다른 코드를 배치할 수 있습니다. 새 약속은 즉시 실행되지만 결과 값은 다음과 같습니다. 첨부파일에서 찾으실 수 있습니다.

그 다음에 아니면 다른 것을 잡거나 잡거나 부착할 수 있습니다. 미리 then의 반환 값 매개변수로 전달합니다. 프라미스를 반환하면 다음 프라미스가 잡거나 프라미스가 이행된 후에 호출됩니다.

그 다음에 아직 완료되지 않은 경우 실행이 완료된 후 then을 추가하여 결과를 반환할 수 있습니다. 내부 함수가 실행됩니다.


왜 필요한가

다른 비동기 함수인 setTimeout이 있지만 함수를 분리하려면 약속이 필요합니다.

setTimeout의 기본 형식을 살펴보겠습니다.


setTimeout의 기본 형식

빨간색으로 표시된 함수는 뺄셈이 안되는 함수입니다. 콜백함수를 이용하여 다음과 같이 표현할 수 있지만 결국 콜백함수는 거기에 있게 됩니다.


그러나 Promise를 사용한다면 다음과 같이 함수를 분리하여 표현할 수 있습니다.


또한 프라미스에 대해 알아야 할 것은 현재 노드 생태계의 모든 콜백 함수가 프라미스로 바뀐다는 것입니다. Promise를 지원하기 위해 대부분의 기능이 변경되고 있습니다.


사용하는 방법

약속 → 새로운 약속

promise
  .then((message) => {
    return new Promise((resolve, reject) => {
      resolve(message);
    });
  })
  .then((message2) => {
    console.log(message2);
    return new Promise((resolve, reject) => {
      resolve(message2);
    });
  })
  .then((message3) => {
    console.log(message3);
  })
  
  .catch((error) => {
    console.log(error);
});

그런 다음 첫 번째에서 메시지를 해결하면 다음에서 Message2로 받을 수 있습니다. 여기에서도 message2가 해결되고 다음으로 message3이 수신됩니다. 그러나 다음 Promise에서 받기 위해서는 새로운 Promise를 반환해야 합니다.


리콜 → 약속

이것을 사용하여 콜백을 약속으로 바꿀 수 있습니다. 다음은 콜백 작성을 위한 템플릿 중 하나입니다.

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

콜백 함수는 세 번 중첩됩니다. 콜백 함수의 각 항목에 들여쓰기를 추가하여 코드 깊이를 늘립니다. 또한 각 콜백 함수는 오류를 개별적으로 처리해야 합니다. 이 코드는 다음으로 대체할 수 있습니다.

function findAndSaveUser(Users) {
  Users.findOne({})
    .then((user) => {
      user.name="JH";
      return user.save();
    })
    .then((user) => {
      return Users.findOne({ gender: 'm' });
    })
    .then((user) => {
      // 생략
    })
    .catch(err => {
      console.log(err);
    });
}

코드는 3단계보다 더 깊어지지 않습니다. 위의 코드에서 then 메서드는 차례로 실행됩니다. 콜백에서 매번 별도로 처리해야 했던 오류도 마지막 catch에서 한 번만 처리할 수 있습니다. 그러나 모든 콜백 함수를 위와 같이 수정할 수 있는 것은 아닙니다. 메서드는 약속을 지원해야 합니다.

예제의 코드는 findOne 메소드와 save 메소드가 내부적으로 Promise 객체를 가지고 있다고 가정했기 때문에 가능합니다.(새로운 Promise는 함수 내부에 구현되어야 합니다.) 나중에 작성하겠습니다.

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

약속하다. 해결하다

Promise.resolve는 즉시 해결되는 약속을 만드는 방법입니다. 비슷한 것을 즉시 거부하는 Promise.reject도 있습니다. 약속이 여러 개인 경우 Promise.all에 삽입하면 모두 해결될 때까지 기다렸다가 계속됩니다. 결과 매개변수에는 각 약속 결과가 배열로 포함됩니다. 약속 중 하나가 거부되면 이제 사업을 시작할 때입니다. 그러나 몇 가지 약속 중 어떤 것이 거부되었는지는 알 수 없습니다.

Promise.all 대신 Promise.allSettled를 사용해야 정확히 어떤 약속이 거부되었는지 알 수 있습니다.

const prmoise1 = Promise.resolve('성공1');
const prmoise2 = Promise.resolve('성공2');
const prmoise3 = Promise.resolve('성공3');
Promise.allSettled((promise1, prmoise2, promise3))
  .then((result) => {
    console.log(result);
/* (
*    { status: 'fulfuiled', value: '성공1' },
*    { status: 'rejected', reason: '성공2' },
*    { status: 'fulfuiled', value: '성공3' },
*  )
*/
  })
  .catch((error) => {
    console.error(error);
  });

Promise.allSettled를 사용하면 상태별로 어떤 약속이 거부되었는지 확인할 수 있도록 결과가 더 자세하게 표시됩니다. 실패의 이유는 이유에 포함되어 있습니다. 따라서 Promise.all 대신 Promise.allSettled를 사용하는 것이 더 좋습니다.

참고로 노드 16부터 거부된 Promise에 catch를 붙이지 않으면 UnhandledPromiseRejection 오류가 발생합니다. 오류가 발생하면 다음 코드가 실행되지 않으므로 Promise에 catch 메서드를 첨부하는 것이 좋습니다.

try {
  Promise.reject('에러');
} catch (e) {
  console.error(e);  // UnhandledPromiseRejection: This error originated either by...
}

Promise.reject('에러').catch(() => {
  // catch 메서드를 붙이면 에러가 발생하지 않음
}

해결하다, 거부하다

마지막으로 중요한 약속하다 행동을 취한다는 뜻입니다. 예를 들어 “00 파일을 읽어라”, “네이버에서 요청을 보내고 다시 오라”. 단, 요청이 실패하는 경우도 있습니다.(네이버가 악성코드로 감지하여 요청을 거부할 수 있습니다.) 따라서 성공하면 해결을 실행한 후 호출하고, 실패하면 거부를 실행하여 요청을 호출합니다. . 그래서 기능이 이중적이라고 할 수 있습니다.


비동기/대기

이 기능은 Node 7.6부터 지원되며 ES2017에 추가되었습니다. 알아두면 정말 편리한 기능이며 Node.js와 같이 비동기식으로 프로그래밍해야 할 때 특히 유용합니다.

약속은 콜백 지옥을 해결하지만 코드는 여전히 장황합니다. 그러면 계속해서 잡고 반복하기 때문입니다. async/await 구문은 약속을 사용하는 코드를 더욱 줄여줍니다.

이전의 Promise 코드를 살펴보겠습니다.

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

콜백과 달리 코드가 깊지는 않지만 코드는 여전히 깁니다. async/await 구문을 사용하여 다음과 같이 변경할 수 있습니다. 비동기 함수를 추가했습니다.

async function findAndSaveUser(Users) {
  let user = await Users.findOne({});
  user.name="JH";
  user = await user.save();
  user = await Users.findOne({ gender: 'm' });
  // 생략
}

코드는 이전보다 훨씬 짧습니다. 함수 선언을 일반 함수 대신 비동기 함수로 바꾼 후 약속 앞에 await를 넣습니다. 이제 함수는 다음 논리로 이동하기 전에 약속이 해결될 때까지 기다립니다. 예를 들어 사용자 변수를 초기화하기 전에 await Users.findOner({})가 해결될 때까지 기다립니다.

위 코드는 에러 처리 부분(Promise가 거부된 경우)이 없으므로 다음과 같은 추가 작업이 필요합니다.

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

try/catch 문으로 논리를 래핑했습니다. Promise의 catch 메서드와 마찬가지로 try/catch 문의 catch는 오류를 처리합니다.

화살표 함수는 비동기와 함께 사용할 수도 있습니다.

const findAndSaveUser = async (Users) => {
  try {
    let user = await Users.finfOne({});
    user.name="JH";
    user = await.user.save();
    user = await.Users.findOne({ gender: 'm' });
    // 생략
  } catch(error) {
    console.error(error);
  }
};

Promise는 async/await와 함께 for 문을 사용하여 순차적으로 실행할 수 있습니다. for 문을 사용하는 것은 노드 10부터 지원되는 ES2018 구문입니다.

const promise1 = Promise.resolve('성공1');
const promise2 = Promise.resolve('성공2');
(async () => {
  for await (promise of (promise1, promise2)) {
    console.log(promise);
  }
})();

이것은 for await of 문을 사용하여 약속 배열을 반복하는 예입니다. 비동기 함수의 반환 값은 항상 약속으로 둘러싸여 있습니다. 실행 후 then을 추가하거나 다른 비동기 함수 내에 await를 추가하여 이를 수행할 수 있습니다.

async function findAndSaveUser(Users) {
  // 생략
}


findAndSaveUser().then(() => { /* 생략 */ });
// 또는
async function other() {
  const result = await findAndSaveUser();
}

앞으로 중첩된 콜백 함수가 있다면 Promise를 사용하여 이를 async/await 구문으로 변환하는 연습을 해야 합니다.