라이브 예제와 함께 JavaScript의 비동기 및 대기

Promise는 나중에 도착하는 값입니다. async 및 wait를 사용하면 페이지가 계속 실행되는 동안 마치 일반적인 단계별 코드인 것처럼 이를 기다리는 코드를 작성할 수 있습니다.

영문 원문 보기

Promise는 서버의 응답과 같이 아직 준비되지 않은 값을 나타냅니다. async 함수는 항상 약속을 반환합니다. 그 안에서 await는 약속이 확정되고 값을 전달할 때까지 해당 기능 하나를 일시 중지합니다. 페이지가 계속 실행됩니다.

먼저 시도해 보세요. 아래 세 가지 작업은 setTimeout로 만든 가짜 요청입니다. 하나씩 달리고, 함께 달리고, 경주로 달리며 총 시간을 비교해 보세요.

실행 예제직접 써 보고 코드를 복사하세요
링크로 공유하기
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>await vs Promise.all vs Promise.race</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .btns { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
  button { font: inherit; padding: 8px 12px; border: 1px solid #c9ced8; border-radius: 8px; background: #fff; cursor: pointer; }
  button:disabled { opacity: .5; cursor: default; }
  .row { display: grid; grid-template-columns: 96px 1fr; align-items: center; gap: 8px; margin: 8px 0; font-size: 14px; }
  .track { position: relative; height: 26px; background: #fff; border: 1px solid #e1e4ea; border-radius: 6px; overflow: hidden; }
  .bar { position: absolute; top: 3px; bottom: 3px; width: 0; border-radius: 4px; background: #93b4f5; }
  .bar.done { background: #2f9e5b; }
  .bar.ignored { background: #c7ccd4; }
  pre { margin: 12px 0 8px; padding: 10px; background: #1d2330; color: #e5e9f0; border-radius: 8px; font-size: 13px; overflow-x: auto; min-height: 4.5em; line-height: 1.5; }
  #total { font-weight: 700; min-height: 1.3em; }
</style>
</head>
<body>
<div class="btns">
  <button data-mode="seq">One by one (await)</button>
  <button data-mode="all">Promise.all</button>
  <button data-mode="race">Promise.race</button>
</div>
<div id="rows"></div>
<pre id="code">Pick a button. Each task is a fake request made with setTimeout.</pre>
<div id="total"></div>

<script>
  // A promise that resolves after ms milliseconds
  const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

  const tasks = [
    { name: 'Load user', ms: 900 },
    { name: 'Load posts', ms: 600 },
    { name: 'Load photos', ms: 1200 },
  ];
  const SCALE = 2800; // the track width stands for 2.8 seconds
  const code = {
    seq: 'for (const t of tasks) {\n  await fakeTask(t); // next one waits\n}',
    all: '// all three start now\nawait Promise.all(\n  tasks.map(fakeTask));',
    race: '// first to settle wins\nconst first = await Promise.race(\n  tasks.map(fakeTask));',
  };

  const rows = document.getElementById('rows');
  const bars = tasks.map((t) => {
    rows.insertAdjacentHTML('beforeend', `<div class="row"><span>${t.name}</span><div class="track"><div class="bar"></div></div></div>`);
    return rows.lastElementChild.querySelector('.bar');
  });

  let t0 = 0;
  // Fake request: draws its own bar while it runs
  async function fakeTask(t) {
    const bar = bars[tasks.indexOf(t)];
    const start = performance.now() - t0;
    bar.style.left = (start / SCALE * 100) + '%';
    const grow = setInterval(() => {
      bar.style.width = ((performance.now() - t0 - start) / SCALE * 100) + '%';
    }, 16);
    await wait(t.ms);
    clearInterval(grow);
    bar.style.width = (t.ms / SCALE * 100) + '%';
    bar.classList.add('done');
    return t.name;
  }

  async function run(mode) {
    bars.forEach((b) => { b.className = 'bar'; b.style.width = '0'; });
    document.getElementById('code').textContent = code[mode];
    const total = document.getElementById('total');
    total.textContent = 'Running...';
    t0 = performance.now();
    let note = '';
    let pending = [];

    if (mode === 'seq') {
      for (const t of tasks) await fakeTask(t);
    } else if (mode === 'all') {
      await Promise.all(tasks.map(fakeTask));
    } else {
      pending = tasks.map(fakeTask);
      note = ' - winner: ' + await Promise.race(pending);
      // The losers keep running; race just stops listening to them
      bars.forEach((b) => { if (!b.classList.contains('done')) b.classList.add('ignored'); });
    }
    total.textContent = 'Total: ' + Math.round(performance.now() - t0) + ' ms' + note;
    await Promise.all(pending); // let the losers finish before the next run
  }

  document.querySelectorAll('button').forEach((btn) => {
    btn.addEventListener('click', async () => {
      document.querySelectorAll('button').forEach((b) => (b.disabled = true));
      await run(btn.dataset.mode);
      document.querySelectorAll('button').forEach((b) => (b.disabled = false));
    });
  });
</script>
</body>
</html>
0.9초, 0.6초, 1.2초의 세 가지 가짜 요청입니다. 하나씩 합산하면 Promise.all이 가장 느리고 Promise.race가 가장 빠릅니다.

약속이란 무엇인가

약속은 세 가지 상태 중 하나에 있습니다. 보류 중 시작됩니다. 그런 다음 값으로 이행되거나 오류로 인해 거부되며 그 이후에는 절대 변경되지 않습니다. Settled는 마지막 두 가지 중 하나에 해당하는 단어입니다.

fetch 및 많은 브라우저 API가 이미 약속을 반환했기 때문에 약속을 직접 작성하는 경우는 거의 없습니다. 가장 많이 작성할 것은 setTimeout을 기다릴 수 있는 것으로 바꾸는 지연 도우미입니다.

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function start() {
  console.log('one');
  await wait(1000);          // this function pauses for a second
  console.log('two');
}
start();
console.log('this runs before "two"');

마지막 줄이 바로 이어집니다. 페이지가 아닌 start만 일시중지됩니다. 이 페이지의 모든 데모는 동일한 트릭을 사용하여 느린 작업을 가짜로 만들기 때문에 네트워크가 필요하지 않습니다.

비동기 함수는 항상 약속을 반환합니다.

함수 앞에 async를 넣으면 일반 문자열을 반환하더라도 해당 반환 값이 약속으로 래핑됩니다. 내부의 throw는 거부된 약속이 됩니다. 이것이 가장 일반적인 비동기 버그가 페이지에서 다음과 같이 보이는 이유입니다.

기다리지 않고 약속 자체를 인쇄합니다. wait를 사용하면 값을 인쇄합니다.
기다리지 않고 약속 자체를 인쇄합니다. wait를 사용하면 값을 인쇄합니다.

[object Promise]는 코드가 값을 원하는 곳에 약속을 사용했음을 의미합니다. 통화 앞에 await를 추가합니다. 호출 코드가 비동기가 아닌 경우 async도 표시하거나 .then(value => ...)를 사용하세요.

await는 비동기 함수 내부 또는 모듈의 최상위 수준에서만 허용됩니다. 다른 곳에서는 스크립트가 실행되기 전에 브라우저가 SyntaxError로 중지됩니다.

모듈에서 최상위 수준 대기

모듈에서는 모든 함수 외부에 await를 작성할 수 있습니다. 모듈은 나머지를 실행하기 전에 해당 줄에서 기다립니다.

<script type="module">
  const res = await fetch('data.json');
  const data = await res.json();
  document.querySelector('h1').textContent = data.title;
</script>

모듈 스크립트는 연기되므로 위의 HTML은 실행될 때 구문 분석되었습니다. 일반 <script>에서는 동일한 코드가 SyntaxError입니다.

모듈 외부에서는 대신 async function main() { ... } main();로 래핑합니다. 응답 텍스트를 객체로 변환하려면 JSON.parse를 참조하세요.

Try/Catch로 오류 잡기

기다렸던 Promise가 거부되면 await 라인이 발생합니다. 일반 try/catch는 이를 포착하므로 async/await가 .then() 및 .catch() 체인보다 더 쉽게 읽을 수 있는 주된 이유입니다.

async function load() {
  try {
    const data = await getData();
    show(data);
  } catch (err) {
    showError(err.message);   // the request failed
  }
}

아무도 기다리거나 잡지 않는 거부된 약속은 처리되지 않은 거부입니다. 브라우저는 이를 콘솔에 오류로 기록하고 페이지는 다음에 일어날 예정인 모든 작업을 자동으로 건너뜁니다.

아래 세 가지 버튼을 사용해 보세요. 실패율을 가끔씩으로 설정하고 각각을 몇 번씩 누르세요.

실행 예제직접 써 보고 코드를 복사하세요
링크로 공유하기
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Async errors: try/catch, allSettled, forgotten await</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; font-size: 14px; }
  .btns { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 12px; }
  button, select { font: inherit; padding: 8px 12px; border: 1px solid #c9ced8; border-radius: 8px; background: #fff; }
  button { cursor: pointer; }
  table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 8px; overflow: hidden; }
  th, td { text-align: left; padding: 7px 10px; border-bottom: 1px solid #e8eaef; }
  th { background: #eef1f5; font-size: 13px; }
  .ok { color: #0f7a3d; font-weight: 600; }
  .bad { color: #b4380f; font-weight: 600; }
  #msg { margin-top: 10px; padding: 10px; border-radius: 8px; background: #fff; border: 1px solid #e1e4ea; min-height: 1.4em; }
  code { font-family: ui-monospace, Consolas, monospace; font-size: 13px; }
</style>
</head>
<body>
<label>Each fake request fails:
  <select id="rate">
    <option value="0.4">Sometimes (40%)</option>
    <option value="0">Never</option>
    <option value="1">Always</option>
  </select>
</label>
<div class="btns">
  <button id="all">try/catch + Promise.all</button>
  <button id="settled">Promise.allSettled</button>
  <button id="forgot">Forgot await</button>
</div>
<table>
  <thead><tr><th>Request</th><th>Result</th></tr></thead>
  <tbody id="rows"><tr><td colspan="2">Press a button.</td></tr></tbody>
</table>
<div id="msg"></div>

<script>
  const names = ['user', 'posts', 'photos'];
  const rows = document.getElementById('rows');
  const msg = document.getElementById('msg');

  // Fake request: random delay, fails at the chosen rate
  function fakeRequest(name) {
    const rate = Number(document.getElementById('rate').value);
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (Math.random() < rate) reject(new Error(name + ': server error'));
        else resolve(name + ' loaded');
      }, 200 + Math.random() * 500);
    });
  }

  function show(list) {
    rows.innerHTML = list.map(([name, text, ok]) =>
      `<tr><td>${name}</td><td class="${ok ? 'ok' : 'bad'}">${text}</td></tr>`).join('');
  }

  // 1. Promise.all inside try/catch: one failure throws, all results are lost
  document.getElementById('all').addEventListener('click', async () => {
    msg.textContent = 'Loading...';
    try {
      const results = await Promise.all(names.map(fakeRequest));
      show(results.map((r, i) => [names[i], r, true]));
      msg.textContent = 'All three succeeded.';
    } catch (err) {
      show(names.map((n) => [n, 'no result (Promise.all rejected)', false]));
      msg.textContent = 'Caught: ' + err.message;
    }
  });

  // 2. allSettled never rejects: one row per request, success or failure
  document.getElementById('settled').addEventListener('click', async () => {
    msg.textContent = 'Loading...';
    const results = await Promise.allSettled(names.map(fakeRequest));
    show(results.map((r, i) => r.status === 'fulfilled'
      ? [names[i], r.value, true]
      : [names[i], r.reason.message, false]));
    const failed = results.filter((r) => r.status === 'rejected').length;
    msg.textContent = failed + ' of ' + results.length + ' failed. The others still count.';
  });

  // 3. The bug: without await you get the Promise object, not the value
  document.getElementById('forgot').addEventListener('click', async () => {
    const wrong = fakeRequest('user');
    wrong.catch(() => {}); // this demo ignores its failure on purpose
    const text = 'Hello ' + wrong;
    msg.innerHTML = 'Without await: <code></code>';
    msg.querySelector('code').textContent = text;
    try {
      const right = await fakeRequest('user');
      show([['user (no await)', text, false], ['user (await)', right, true]]);
    } catch (err) {
      show([['user (no await)', text, false], ['user (await)', err.message, false]]);
    }
  });
</script>
</body>
</html>
선택한 속도로 실패하는 가짜 요청입니다. Promise.all은 한 번의 실패로 모든 것을 잃고, allSettled는 모든 결과를 유지하며, 마지막 버튼에는 누락 대기 버그가 표시됩니다.

순차적으로 또는 병렬로

비동기 함수를 호출하면 작업이 즉시 시작됩니다. await는 기다릴 때만 결정합니다. 따라서 세 개의 대기가 연속적으로 실행되고, 세 개 모두 먼저 시작한 다음 대기가 겹치게 됩니다.

세 개가 연속으로 추가됩니다. Promise.all은 가장 느린 요청 동안만 기다립니다.
세 개가 연속으로 추가됩니다. Promise.all은 가장 느린 요청 동안만 기다립니다.

예를 들어 사용자를 로드한 다음 해당 사용자의 게시물을 로드하는 등 단계에 이전 결과가 필요할 때 차례로 사용합니다. 그렇지 않으면 네 가지 연결자 중 하나를 사용하여 시작하십시오.

방법 다음과 같은 경우에 해결됩니다. 결과 다음에 좋습니다
Promise.all 모두 이행하거나 첫 번째 거부 값 배열 모든 것은 성공해야 한다
Promise.allSettled 모든 약속은 확정됐어 { status, value } 또는 { status, reason } 어레이 무엇이 효과가 있었는지 보여주고, 무엇이 실패했는지 보고하세요
Promise.race 첫 번째가 정착, 성공 또는 실패 그 하나의 값이나 오류 시간 초과
Promise.any 첫 번째는 충족합니다. 해당 값 또는 모두 거부되는 경우 AggregateError 여러 소스, 첫 번째 좋은 답변이 승리

그들 중 누구도 듣지 않는 약속을 취소하지 않습니다. 위의 레이스 데모에서는 느린 바가 끝까지 계속 실행됩니다. 그 결과는 단순히 무시됩니다.

루프에서 기다리다

내부에 await가 포함된 for...of는 루프 본문을 한 번에 한 항목씩 실행합니다. 주문이 중요한 경우에는 맞습니다. 하지만 항목이 50개 있으면 연속으로 50번의 대기가 필요합니다.

for...of는 각각을 기다리고, Promise.all을 사용한 map은 이를 함께 실행하며, forEach는 전혀 기다리지 않습니다.
for...of는 각각을 기다리고, Promise.all을 사용한 map은 이를 함께 실행하며, forEach는 전혀 기다리지 않습니다.
// One at a time: total = sum of all
for (const id of ids) {
  await save(id);
}

// Together: total = the slowest one
await Promise.all(ids.map((id) => save(id)));

비동기 콜백으로 forEach를 피하세요. 콜백이 반환하는 약속을 무시하므로 항목이 완료되기 전에 루프 이후의 코드가 실행됩니다.

시간 초과 및 취소

지연 도우미가 포함된 Promise.race는 모든 약속에 시간 제한을 제공합니다.

const timeout = (ms) => new Promise((_, reject) =>
  setTimeout(() => reject(new Error('Timed out')), ms));

const data = await Promise.race([getData(), timeout(5000)]);

그러면 작업이 아니라 기다림이 중단됩니다. 실제로 요청을 중지하려면 AbortController를 사용하세요. signal를 요청에 전달하고 더 이상 응답이 필요하지 않으면 abort()를 호출합니다. 그런 다음 fetch는 name가 'AbortError'인 오류와 함께 거부합니다.

const controller = new AbortController();
const res = await fetch('/api/search?q=pear', { signal: controller.signal });
// elsewhere, when the user types something newer:
controller.abort();

fetch에 대한 일반 시간 제한의 경우 AbortSignal.timeout(5000)는 자체적으로 중단되는 신호를 만듭니다. 이 경우 오류의 name는 'TimeoutError'입니다.

완성된 예: 검색창

검색창은 이 모든 것을 하나로 묶어줍니다. 입력하면 요청이 전송되고, 답변은 나중에 도착하며, 오래되고 느린 답변이 새 답변을 덮어쓰면 안 됩니다.

실행 예제직접 써 보고 코드를 복사하세요
링크로 공유하기
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Async search box with cancel</title>
<style>
  body { margin: 0; padding: 16px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; font-size: 14px; }
  input[type=search] { width: 100%; box-sizing: border-box; font: inherit; font-size: 16px; padding: 10px 12px; border: 1px solid #c9ced8; border-radius: 8px; }
  .opt { display: block; margin: 8px 0; color: #4b5563; }
  #status { min-height: 1.4em; margin: 8px 0; }
  #status.loading::before { content: ''; display: inline-block; width: 10px; height: 10px; margin-right: 6px; border: 2px solid #93b4f5; border-top-color: transparent; border-radius: 50%; animation: spin .7s linear infinite; }
  #status.error { color: #b4380f; font-weight: 600; }
  @keyframes spin { to { transform: rotate(360deg); } }
  ul { list-style: none; margin: 0; padding: 0; background: #fff; border-radius: 8px; border: 1px solid #e1e4ea; min-height: 40px; max-height: 190px; overflow: auto; }
  li { padding: 7px 12px; border-bottom: 1px solid #eef0f3; }
  #log { margin-top: 10px; font: 12px/1.5 ui-monospace, Consolas, monospace; color: #6b7280; max-height: 92px; overflow: auto; }
</style>
</head>
<body>
<input type="search" id="q" placeholder="Search fruit (type 'oops' for an error)" autocomplete="off">
<label class="opt"><input type="checkbox" id="cancel" checked> Cancel stale requests (AbortController)</label>
<div id="status">Start typing.</div>
<ul id="results"></ul>
<div id="log"></div>

<script>
  const FRUIT = ['Apple', 'Apricot', 'Avocado', 'Banana', 'Blackberry', 'Blueberry', 'Cherry', 'Coconut',
    'Date', 'Fig', 'Grape', 'Grapefruit', 'Guava', 'Kiwi', 'Lemon', 'Lime', 'Lychee', 'Mango', 'Melon',
    'Nectarine', 'Orange', 'Papaya', 'Peach', 'Pear', 'Pineapple', 'Plum', 'Pomegranate', 'Raspberry', 'Strawberry'];

  // Fake search API. Short queries are slower, like a real server with more matches.
  // It listens to an AbortSignal and gives up when told to.
  function fakeSearch(query, signal) {
    return new Promise((resolve, reject) => {
      const ms = Math.max(200, 1800 - query.length * 600) + Math.random() * 150;
      const timer = setTimeout(() => {
        if (query.includes('oops')) return reject(new Error('Search is down. Try again.'));
        resolve(FRUIT.filter((f) => f.toLowerCase().includes(query.toLowerCase())));
      }, ms);
      signal?.addEventListener('abort', () => {
        clearTimeout(timer);
        reject(new DOMException('Cancelled', 'AbortError'));
      });
    });
  }

  const input = document.getElementById('q');
  const status = document.getElementById('status');
  const list = document.getElementById('results');
  const log = (text) => document.getElementById('log').insertAdjacentHTML('afterbegin', text + '<br>');
  let controller = null;
  let debounce = 0;
  let id = 0;

  async function search(query) {
    const n = ++id;
    if (document.getElementById('cancel').checked) {
      controller?.abort();               // cancel the previous request
      controller = new AbortController();
    } else {
      controller = null;
    }
    status.className = 'loading';
    status.textContent = 'Searching for "' + query + '"...';
    const started = performance.now();
    try {
      const found = await fakeSearch(query, controller?.signal);
      list.replaceChildren(...found.map((f) => {
        const li = document.createElement('li');
        li.textContent = f;
        return li;
      }));
      status.className = '';
      status.textContent = found.length + ' results for "' + query + '" in ' + Math.round(performance.now() - started) + ' ms';
      log('#' + n + ' "' + query + '" shown');
    } catch (err) {
      if (err.name === 'AbortError') return log('#' + n + ' "' + query + '" cancelled');
      status.className = 'error';
      status.textContent = err.message;
      list.innerHTML = '';
      log('#' + n + ' "' + query + '" failed');
    }
  }

  // Debounce: wait until typing pauses for 300 ms
  input.addEventListener('input', () => {
    clearTimeout(debounce);
    const query = input.value.trim();
    if (!query) { controller?.abort(); list.innerHTML = ''; status.className = ''; status.textContent = 'Start typing.'; return; }
    debounce = setTimeout(() => search(query), 300);
  });
</script>
</body>
</html>
로딩 상태, 오류 상태("죄송합니다" 유형) 및 취소된 오래된 요청으로 가짜 검색을 디바운스했습니다. 상자를 선택 취소하고 "a"를 입력하고 잠시 멈춘 다음 "p"를 입력하면 이전 답변이 새 답변을 덮어쓰는 것을 볼 수 있습니다.
  • 디바운스: addEventListener와 연결된 각 input 이벤트는 300ms 타이머를 재설정합니다. 입력이 일시 중지된 경우에만 검색이 실행됩니다.

  • 로드 중 상태: await 앞에 "검색 중..." 메시지를 설정하고 이후에 교체합니다.

  • 오래된 요청 취소: 새 검색을 할 때마다 이전 컨트롤러에서 abort()를 호출합니다. 취소된 검색은 AbortError와 함께 catch에 도달하고 조용히 돌아옵니다.

  • 오류: catch에 있는 다른 항목은 빈 목록 대신 메시지로 표시됩니다.

기다리고 있는 작업에 신호를 전달할 수 없는 경우 요청 카운터가 동일한 작업을 수행합니다. 숫자를 유지하고, 검색할 때마다 숫자를 늘리고, 숫자가 최신이 아닌 답변은 무시하세요.

작동하지 않을 때

당신이 보는 것 원인 수정
페이지의 [object Promise] 약속은 await 없이 사용되었습니다. await 통화 또는 .then() 사용
콘솔의 "Uncaught (in promise)" catch 없이 거부된 약속 await 주변의 try/catch 또는 .catch()
대기를 언급하는 SyntaxError 일반 함수 또는 클래식 스크립트의 await async 기능을 표시하거나 type="module"를 사용하세요.
요청 루프에는 시간이 걸립니다 for...of 내부의 await가 하나씩 실행합니다. 독립된 경우 map + Promise.all
루프 이후의 코드가 너무 일찍 실행됩니다. 비동기 콜백을 사용하는 forEach for...of 또는 Promise.all
이전 검색결과가 새 검색결과를 대체합니다. 더 느리고 오래된 요청이 마지막에 완료되었습니다. AbortController 또는 요청 카운터
한 번의 실패로 인해 모든 결과가 손실됩니다. Promise.all는 첫 번째 실패 시 거부합니다. Promise.allSettled

아무것도 하지 않는 스크립트의 경우 HTML JavaScript가 작동하지 않음은 다른 일반적인 원인을 따릅니다.

링크로 공유하세요

비동기 동작은 스크린샷으로 설명하기 어렵습니다. 로딩 상태, 오류 및 도착한 주문 결과는 페이지가 실행될 때만 표시됩니다.

작업 버전을 보내려면 페이지를 NOS 문서에 붙여넣고 공유 링크 만들기를 선택하세요. 링크할 HTML이 이를 안내합니다.

페이지는 작성된 대로 렌더링되고 스크립트가 실행되므로 페이지를 받는 사람은 버튼을 누르고 검색 상자에 직접 입력할 수 있습니다. 나중에 코드를 변경하면 동일한 링크에 새 버전이 표시됩니다.

자주 묻는 질문

비동기 함수는 항상 Promise를 반환하나요?

그렇습니다. 당신이 반환하는 것은 무엇이든 Promise로 포장되고, 그 안에 던져진 모든 것은 거부된 Promise가 됩니다. 값을 얻으려면 다른 비동기 함수나 모듈 내에서 호출을 기다리거나 .then()을 사용하세요.

대기가 전체 페이지를 차단합니까?

아니요. wait는 해당 기능이 있는 비동기 기능만 일시 중지합니다. 클릭, 타이머, 애니메이션 및 기타 기능은 계속 실행됩니다. Promise가 확정되면 일시 중지된 함수가 계속됩니다.

비동기 함수 외부에서 Wait를 사용할 수 있나요?

고전적인 스크립트에서는 그렇지 않습니다. 구문 오류입니다. <script type="module">와 같은 모듈의 최상위 수준에서는 그렇습니다. 다른 곳에서는 코드를 비동기 함수로 래핑합니다.

Promise.all과 Promise.allSettled의 차이점은 무엇입니까?

Promise.all은 하나의 Promise가 거부되자마자 거부하므로 다른 결과는 손실됩니다. Promise.allSettled는 항상 모든 약속을 기다리고 상태가 "이행됨", 값 또는 "거부됨" 및 이유가 포함된 약속당 객체를 제공합니다.

Wait를 사용한 루프가 왜 그렇게 오래 걸리나요?

for...of 내부의 대기는 다음 항목을 시작하기 전에 각 항목을 기다리므로 총 시간은 모든 항목의 합계입니다. 항목이 서로 의존하지 않는 경우 map으로 시작하고 대신 Promise.all을 기다립니다.

함께 읽어 보기