비동기식/대기 및 약속에 대한 질문

JavaScript는 비동기식 특성을 유지합니다.대부분의 웹 API는 동기화되지만 자바스크립트의 함수first-class citizens가 나타나면서 상황이 달라졌다.현재 기본적으로 모든 새로운 JavaScript API는 비동기적으로 설계되어 있습니다.(수십 년의 역사를 가진 cookies API도 얻을 수 있다an asynchronous re-vamp
우리가 이러한 비동기 임무를 서열화해야 할 때 문제가 발생할 수 있다. 이것은 리셋이 끝날 때 비동기 방법을 집행하는 것을 의미한다.실제로 우리는 반드시 이렇게 해야 한다.
$.get('/api/movies/' + movieCode, function(movieData) {
  $.get('/api/directors/' + movieData.director, function(directorData) {
    $.get('/api/studios/' + directorData.studio, function(studioData) {
      $.get('/api/locations/' + studioData.hq, function(locationData) {
        // do something with locationData
      });
    });
  });
});
네, 그것은 마지막 피라미드입니다.(이것은 단지 간단한 예일 뿐이다. 비동기 임무를 병행해야 할 때 일은 미친 듯이 변한다.)
그리고 Promises는 ES2015와 함께 나타났습니다.그리고우리 코드를 이렇게 만들겠다고 약속해줘.
doSomething()
  .then(data => doStuff(data))
  .then(result => doOtherStuff(result))
  .then(outcome => showOutcome(outcome));
좋다실천 과정에서 우리는 종종 이런 결과를 얻는다.
doSomething().then(data => {
  doStuff(data).then(result => {
    doOtherStuff(data, result).then(outcome => {
      showOutcome(outcome, result, data);
    });
  });
});
또 피라미드다!무슨 일이야?!
하나의 임무가 이전 임무의 결과에 달려 있을 뿐만 아니라, 이전 임무의 결과에 달려 있을 때, 기본적으로 이런 상황이 발생할 것이다.물론 너는 이렇게 할 수 있다.
let _data;
let _result;
doSomething().then(data => {
  _data = data;
  return doStuff(data);
}).then(result => {
  _result = result;
  return doOtherStuff(_data, result);
}).then(outcome => {
  showOutcome(outcome, _result, _data);
});
나는 심지어 이것이 얼마나 어색하고 귀에 거슬리는지를 지적하기 시작하지 않을 것이다.값을 부여하기 전에 우리는 먼저 우리가 필요로 하는 변수를 성명한다. 만약 당신이 나와 마찬가지로'반드시 사용해야 한다-const'는 강박증을 앓고 있다면 변수의 값이 변하지 않을 것으로 예상될 때마다 이것let이 당신의 눈동자를 찔렀다고 느낄 것이다.
그런데 ES2016이 와서 async/await의 달콤함을 가져왔어요!이 약속(...)우리의 혼란을 유사한 동기화 코드로 바꾸기
const data = await doSomething();
const result = await doStuff(data);
const outcome = await doOtherStuff(data, result);
await showOutcome(outcome, result, data);
좋아요!
하지만...여느 때와 마찬가지로 일이 항상 그렇게 쉽지는 않다.어디 보자.

약속은 잊혀져서는 안 된다


약속을 거절하는 것은 실수를 일으키지 않기 때문에 특히 옳다.브라우저와 노드가 최근에 더욱 스마트해졌지만, 처리되지 않은 거부 약속은 보통 소리 없이 실패할 것이다.치명적이야.디버깅의 혼란은 말할 것도 없다.
지금await이 약속을 거절할 때 무슨 일이 일어날까요?
던져버려.
그래서 너는 이 문제를 해결하는 것이 매우 쉽다고 생각할 수도 있다.Dell은 try...catch만 년을 보유하고 있습니다.
try {
  const data = await doSomething();
} catch (e) {
  console.error('Haha, gotcha!', e.message);
}
... 지금 물어봐야겠어요.당신들 중 몇 명의 자바스크립트 개발자가 편집try...catches에 만족합니까?JavaScript는 항상 너그러운 언어로, 대부분의 경우 값이 null 인지, 아니면 비슷한 값인지 확인하기만 하면 된다.또한 처리try...catch를 할 때 자바스크립트의 성능이 좋지 않아 어색한 반응을 보일 수 있습니다.
(최근 상황이 좀 달라졌지만. V8이 이전에 내부 코드를 최적화하지 않았지만try...catch V8 6.0과 크롬 60과 노드 8.3에 덧붙인 터보팬은 더 이상 이런 상황이 아니다. 다른 브라우저 공급업체들이 곧 따라잡을 것 같다.)따라서 우리는 보통performance problems of native Promise s으로 끝낼 것이다.)

광범위한 재해


좋습니다. 우리niceawait 한 줄을 5줄try...catch으로 바꿔야 합니다.이것은 이미 충분히 야단났지만, 불행하게도, 이것은 아직 전부가 아니다.코드를 다시 확인합니다.
try {
  const data = await doSomething();
} catch (e) { ... }

// Doing something with data...
좋아, 우리는 다시 운이 좋아졌다. 우리는 사용할 수 없다data. 왜냐하면 그것은 우리의 범위를 넘어섰기 때문이다!사실, 그것의 범위는 try 거리에만 존재한다!우리는 어떻게 이 문제를 해결합니까?
... 솔루션은 또 추악하다.
let data;
try {
  data = await doSomething();
} catch (e) { ... }

// Doing something with data...
다시 사용let예성명 변수...한 사람이 거의 다시 사용해야 한다var!사실상, 그것은 그렇게 나쁘지 않을 것이다. 왜냐하면 async/await 를 사용하면, 당신의 함수는 평탄한 작용역이 있을 수 있고, 당신의 변수는 어쨌든 폐쇄 작용역이 있을 수 있기 때문이다.하지만 린트는 당신의 코드가 엉망이고, 당신의 강박증은 당신을 잠들게 하지 않으며, 커피는 시큼해지고, 고양이는 슬퍼할 것이라고 알려줄 것이다.
우리가 얻은 유일한 진보는 let 블록을 사용하기 전에 try...catch 사용할 수 있다는 것이다. 그래서 일이 그렇게 불안하지 않다.
let data;
try {
  data = await doSomething();
} catch (e) { ... }

let result;
try {
  result = await doStuff(data);
} catch (e) { ... }

포켓몬스터 솔루션


만약 네가 고양이의 즐거움에 관심이 있다면, 너는 무엇을 해야 한다.다음은 흔히 볼 수 있는 간단한 f-that-I-ve-stuff-to-do 방식입니다.
try {
  const data = await doSomething();
  const result = await doStuff(data);
  const outcome = await doOtherStuff(data, result);
  await showOutcome(outcome, result, data);
} catch(e) {
  console.error('Something went wrong, deal with it 🕶¸', e.message);
}
내가 너에게 말하지만, 너는 여전히 잠을 이루지 못한다.그래, 너는 반드시 그들을 잡아야 한다. 그러나 그렇지 않다.너는 이미 수없이 가르침을 받았다. 이것은 좋지 않다. 너는 좋지 않다고 느낄 것이다. 특히 자바스크립트에서 여러 개의 catch 블록에 의존해서 이상 유형을 구분해서는 안 된다. 반대로, 너는 instanceof 심지어 message 속성으로 그것들을 검사해야 한다.

규정대로 일을 처리하다


네 새끼손가락은 네가 영원히 그렇게 하지 않고 마땅히 해야 할 일을 하겠다고 약속했다.가능한 경우:
try {
  const data = await doSomething();
  const result = apparentlyInnocentFunction(data);
  return result;
} catch(e) {
  console.error('Error when doingSomething, check your data', e.message);
}
우리는 거절당한 약속을 잡았다. 맞아.그런데 그 뒤에 무슨 일이 있었어요?아무것도 아닙니다. 우리는 단지 무고한 함수를 호출해서 데이터를 변환할 뿐입니다.
... 확실합니까?그 기능이 정말 그렇게 무고합니까?
문제는 atry...catch가 여전히 atry...catch라는 것이다.그것은 await ed 약속을 포착할 뿐만 아니라, 우리가 그것을 기대하든 말든 모든 오류를 포착할 것이다.올바른 일을 하기 위해서는 try...catch로 포장awaited공약을 해야 한다.
추하다.지루했어괴롭습니다.하지만 필요한 거야.
우리가 Promises를 사용할 때 이미 이 점을 보았기 때문에 이것은 새로운 것이 아니다.요컨대 이렇게 하지 마라.
doSomething.then(data => {
  const result = apparentlyInnocentFunction(data);
  return result;
}).catch(error => {
  console.error('Error when doingSomething, check your data', e.message);
});
다음과 같이 변경합니다.
doSomething.then(data => {
  const result = apparentlyInnocentFunction(data);
  return result;
}, error => { // <= catching with the second argument of `then`!
  console.error('Error when doingSomething, check your data', e.message);
});

좋은 타협?


그렇다면 우리는 이 혼란에 어떻게 대처해야 하는가?하나의 좋은 해결 방안은 try...catch 블록을 완전히 제거하고 Promise 블록을 이용하여 자신들이 가지고 있는 catch 방법을 기억하고 다시 돌아올 수 있다는 것이다Promise.우리는 도착했다.
const data = await doSomething()
    .catch(e => console.error('Error when doingSomething', e.message));
if (!data) { /* Bail out somehow */ }
개인적으로 나는 이것에 대해 몸소 느낀다.더 좋아요?저희가 혼합 기술인가요?나는 이것이 주로 우리가 무엇을 처리하느냐에 달려 있다고 생각한다. 그러니 너에게 줄게.
다음 사항에 유의하십시오.
  • await 해석Promises뿐만 아니라 then 방법을 가진 대상인 thenable(이것을 시험해 보자:await {then() {console.log('Foo!')}}).
  • 이외에 당신은 await 어떤 대상, 심지어 문자열 또는 null도 할 수 있습니다.
  • 이것은 then 또는 catch가 정의가 없거나 당신이 생각하는 것이 아닐 수도 있다는 것을 의미한다..catch(f).then(null, f)의 설탕이기 때문에 표를 정의하는 데는 후자만 필요하다는 것을 기억해야 한다.)

    숨겨진 평행성


    어떻게 여러 개의 병행(또는 더 좋은 병발) 약속을 동시에 해결합니까?Dell은 항상 Promise.all:
    Promise.all([ doSomething(), doSomethingElse() ]).then(...);
    
    // or in terms of await:
    await Promise.all([ doSomething(), doSomethingElse() ]);
    
    하지만 Cory House는 최근 다음과 같은 프롬프트를 제공합니다.

    콜리 하우스 호텔🏠
    @호스콜

    알림: async/await를 사용하면 여러 개의 비동기 동작을 병행으로 실행할 수 있습니다.어떻게 써요?대기 성명을 같은 곳에 두기... twitter.com/i/web/status/9…
    2017년 11월 13일 오후 16:20
    따라서 그것 없이도 합병 공약을 해결할 수 있다.
    const a = doSomething();
    const b = doSomethingElse();
    // Just like await Promise.all([a, b])
    await a, await b;
    
    이 비결은 약속await이 실행되기 전에 이미 시작되었다는 것이다. ab가 아닌 함수 호출을 직접 기다리면 서열화 실행이 이루어진다.
    나의 건의는 이러한 가능한 병발 문제에 주의하는 것이다.똑똑하게 이 점을 이용하려 하지 마라.읽기 용이성Promise.all 측면에서 사용이 더욱 뚜렷하다.

    설탕뿐만 아니라.

    async/await 는 자바스크립트의 많은 다른 새로운 기능과 마찬가지로, 단지 고전적인 ES5 자바스크립트의 문법에 대한 보충일 뿐이라는 것을 들은 적이 있을 것이다.이것은 기본적으로 정확하지만, 많은 다른 상황 (클래스, 화살표 함수 등) 처럼, 그것은 더 많다.
    Mathias Bynensrecently pointed out의 말대로 JS 엔진은 반드시 대량의 작업을 해야만 Promise체인에서 적당한 창고 추적을 얻을 수 있기 때문에 async/await를 사용하는 것이 더 좋은 선택임에 틀림없다.
    문제는 우리가 그것을 마음대로 사용할 수 없다는 것이다.IE 또는 Node 6 같은 오래된 브라우저를 지원해야 합니다.새 문법의 x가 지원되지 않습니다.그러나 UC와 삼성인터넷don't support it either 등 브라우저를 소홀히 해서는 안 된다!마지막으로, 우리는 그것을 전부 전송해야 하며, 일정 시간 내에 이렇게 할 것이다.
    업데이트(2018년 3월): 삼성 인터넷과 UC 브라우저는 현재 모두 async/await를 지원하지만 구 버전에 주의해야 한다.

    결론


    나는 너의 것을 모르지만transpiledasync 함수에 대한 나의 경험은...지금까지는 그다지 이상적이지 않다.크롬이 소스맵을 처리할 때 버그가 있거나, 정의가 잘 안 된 것 같지만, 어쨌든.async/await를 사용하고 있습니까?네, 물론입니다. 하지만 제가 생각했던 것처럼 그것을 많이 사용하지 않았습니다. 왜냐하면 모든 문제가 언급되었기 때문입니다.이것은 틀림없이 미래이지만, 미래는 반드시 신중하게 대처해야 한다.
    당신은 async/await에 대해 어떤 경험을 가지고 있습니까?

    좋은 웹페이지 즐겨찾기