асинхронная функция (сложности на собеседовании?)

JavaScript

async function

: используется для определения асинхронной функции, которая возвращает объект AsyncFunction. Асинхронная функция — это функция, которая выполняется асинхронно через цикл обработки событий и возвращает результат через неявное обещание. Если вы используете асинхронные функции в своем коде, вы обнаружите, что их синтаксис и структура будут больше похожи на стандартные синхронные функции.

  • Вы также можете использовать выражения асинхронных функций для определения асинхронных функций.
function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  console.log('calling');
  var result = await resolveAfter2Seconds();
  console.log(result);
  // expected output: 'resolved'
}

asyncCall();

Синтаксис асинхронной функции:

async function name([param[, param[, ... param]]]) { statements }

параметр

  • имя имя функции.
  • параметр Аргументы для передачи в функцию.
  • заявления Оператор тела функции.

возвращаемое значение

Возвращенный объект Promise либо разрешает возвращаемый результат асинхронной функции, либо отклоняет, если асинхронная функция выдает исключение.

описывать

  异步函数可以包含await指令,该指令会暂停异步函数的执行,并等待Promise执行,
  然后继续执行异步函数,并返回结果。
  • Помните, что ключевое слово await допустимо только внутри асинхронных функций. Если вы используете его вне асинхронной функции, будет выдана синтаксическая ошибка.

Обратите внимание, что когда приостановлена ​​асинхронная функция, функция, которую она вызывает, продолжает выполнять (получает неявное обещание, возвращенное Async Function)

  • Целью async/await является упрощение поведения синхронизации при использовании множества промисов и выполнении определенных операций с набором промисов. Подобно структуре промисов обратного вызова, async/await больше похож на комбинацию генераторов и промисов.

Пример

var resolveAfter2Seconds = function() {
  console.log("starting slow promise");
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("slow");
      console.log("slow promise is done");
    }, 2000);
  });
};

var resolveAfter1Second = function() {
  console.log("starting fast promise");
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("fast");
      console.log("fast promise is done");
    }, 1000);
  });
};

var sequentialStart = async function() {
  console.log('==SEQUENTIAL START==');

  // 1. Execution gets here almost instantly
  const slow = await resolveAfter2Seconds();
  console.log(slow); // 2. this runs 2 seconds after 1.

  const fast = await resolveAfter1Second();
  console.log(fast); // 3. this runs 3 seconds after 1.
}

var concurrentStart = async function() {
  console.log('==CONCURRENT START with await==');
  const slow = resolveAfter2Seconds(); // starts timer immediately
  const fast = resolveAfter1Second(); // starts timer immediately

  // 1. Execution gets here almost instantly
  console.log(await slow); // 2. this runs 2 seconds after 1.
  console.log(await fast); // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
}

var concurrentPromise = function() {
  console.log('==CONCURRENT START with Promise.all==');
  return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then((messages) => {
    console.log(messages[0]); // slow
    console.log(messages[1]); // fast
  });
}

var parallel = async function() {
  console.log('==PARALLEL with await Promise.all==');
  
  // Start 2 "jobs" in parallel and wait for both of them to complete
  await Promise.all([
      (async()=>console.log(await resolveAfter2Seconds()))(),
      (async()=>console.log(await resolveAfter1Second()))()
  ]);
}

// This function does not handle errors. See warning below!
var parallelPromise = function() {
  console.log('==PARALLEL with Promise.then==');
  resolveAfter2Seconds().then((message)=>console.log(message));
  resolveAfter1Second().then((message)=>console.log(message));
}

sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"

// wait above to finish
setTimeout(concurrentStart, 4000); // after 2 seconds, logs "slow" and then "fast"

// wait again
setTimeout(concurrentPromise, 7000); // same as concurrentStart

// wait again
setTimeout(parallel, 10000); // truly parallel: after 1 second, logs "fast", then after 1 more second, "slow"

// wait again
setTimeout(parallelPromise, 13000); // same as parallel

ожидание и параллелизм

  • В sequenceStart программа приостанавливается на 2 секунды в первом ожидании, а затем на 1 секунду во втором ожидании. Второй таймер не создается до тех пор, пока не истечет срок действия первого таймера. Программа выполняется за 3 секунды.

  • В concurrentStart одновременно создаются два таймера, а затем выполняется await. Оба таймера запускаются одновременно, что означает, что для завершения работы программы требуется всего 2 секунды, а не 3 секунды, которые являются временем самого медленного таймера.

  • Но ожидание по-прежнему выполняется последовательно, и второе ожидание все равно должно дождаться завершения первого. В этом примере это приводит к тому, что вывод, который завершается первым, появляется после самого медленного вывода.

  • Если вы хотите выполнять две или более задач параллельно, вы должны использовать await Promise.all([job1(), job2()]) как в параллельном режиме.

async/await vs Promise#then и обработка ошибок

  • Большинство асинхронных функций также можно написать с использованием промисов. Однако с точки зрения обработки ошибок асинхронные функции легче перехватывать ошибки исключения.

  • И функция concurrentStart, и функция concurrentPromise в приведенном выше примере функционально эквивалентны. В функции concurrentStart, если какой-либо ожидаемый вызов терпит неудачу, она автоматически перехватывает исключение, выполнение асинхронной функции прерывается, и ошибка передается вызывающей стороне путем неявного возврата обещания.

  • Это также происходит в примере с промисом, где функция должна отвечать за возврат промиса, фиксирующего завершение функции. В функции concurrentPromise это означает, что она возвращает обещание из Promise.all([]).then(). Фактически, предыдущие версии этого примера забыли сделать это!

  • Однако асинхронная функция по-прежнему может игнорировать ошибки по ошибке.

  • Возьмем в качестве примера параллельную асинхронную функцию. Если он не ожидает (или не возвращает) результат вызова Promise.all([]), никакие ошибки распространяться не будут. Хотя пример функции parallelPromise выглядит просто, он вообще не обрабатывает ошибки! Для этого требуется обработчик, аналогичный return Promise.all([]).

Перепишите цепочку обещаний с помощью асинхронной функции

  • API, который возвращает обещание, создаст цепочку обещаний, которая разбивает функцию на части. Например следующий код:
function getProcessedData(url) {
  return downloadData(url) // 返回一个 promise 对象
    .catch(e => {
      return downloadFallbackData(url)  // 返回一个 promise 对象
    })
    .then(v => {
      return processDataInWorker(v); // 返回一个 promise 对象
    });
}
  • Можно переписать как одну асинхронную функцию:
async function getProcessedData(url) {
  let v;
  try {
    v = await downloadData(url); 
  } catch (e) {
    v = await downloadFallbackData(url);
  }
  return processDataInWorker(v);
}

Обратите внимание, что в приведенном выше примере в операторе return нет оператора await, потому что возвращаемое значение асинхронной функции будет неявно передано в Promise.resolve.

Сравнение return await promiseValue и return promiseValue;

Возвращаемое значение неявно передается в Promise.resolve, что не означает, что return await promiseValue и return promiseValue функционально одинаковы.

  • Взгляните на приведенный выше код, переписанный ниже, который возвращает null, когда processDataInWorker выдает исключение:
async function getProcessedData(url) {
  let v;
  try {
    v = await downloadData(url);
  } catch(e) {
    v = await downloadFallbackData(url);
  }
  try {
    return await processDataInWorker(v); // 注意 `return await` 和单独 `return` 的比较
  } catch (e) {
    return null;
  }
}

Простое написание return processDataInworker(v); приведет к тому, что функция вернет обещание, а не null, когда processDataInWorker(v) завершится ошибкой. Есть некоторые тонкие различия между return foo; и return await foo;: return foo; вернет foo напрямую, независимо от того, является ли foo обещанием или отказом. И наоборот, если foo является промисом, return await foo; будет ждать, пока foo разрешится или отклонится, и в случае отклонения вызовет исключение перед возвратом.

Справочная документация:developer.Mozilla.org/this-cn/docs/…

Автор сенсационных заголовков? ? ? ? ?

Добро пожаловать, чтобы внести свой вклад, добро пожаловать, чтобы пожаловаться, спасибо за вашу поддержку ღ( ´・ᴗ・` )