Как «Отцы и дети» связаны с функцией ES6 Async / Await? Итак - это довольно просто - Сыновья. А обещания - отцы.

Даже если вы никогда не читали эту книгу, вы должны понять проблему - старое поколение не может понять молодежь, молодежь не поймет, что «старые глупые порождения» на самом деле дали им рождение. И тогда они повторят судьбу отцов.

В то же время - они такие же. Просто не могу этого понять. Но вернемся к async / await.

Происхождение async / await

Поскольку async является новой функцией ES6, она по-прежнему основана на Promise. Если у вас асинхронный - вы вернете обещание, а если вы ждете - вы будете ждать обещания.

Единственное отличие - генераторы. Затем вы чего-то ждете - вы просто приостанавливаете выполнение текущей функции, а затем продолжаете с того же места. Так работают генераторы - как приостанавливаемая (возобновляемая) функция.

Поток управления

Основное различие между async / await и Promises заключается в том, как они работают. Здесь можно найти красивое объяснение, но позвольте повторить.

async function solution() {
    // "blocking" call
    console.log(await rp('http://example.com/'));

    // Spawn the HTTP concurrently
    const call2Promise = rp('http://example.com/');  // no wait
    const call3Promise = rp('http://example.com/');  // no wait

    // After they are both spawn - wait for both of them
    const response2 = await call2Promise;    
    const response3 = await call3Promise; 
}

То же и в обещаниях:

rp('http://example.com/')
   .then(result1 => {
    // Executes after the first request has finished
    console.log(result1);

    return Promise.all([
       rp('http://example.com/'), 
       rp('http://example.com/')
    ]);
});

Итак - async / await - это круто, если вы выполняете что-то пошагово, но не вещь, если у вас есть что-то более сложное, потому что вам нужно добавить какой-то странный код, и вы должны понять, как все это работает, чтобы получить хороший результат.

Сложные вещи по-прежнему выполнимы, но вы начнете использовать ... обещания.

// Encapsulate the solution in an async function
async function solution() {
    console.log(await rp('http://example.com/'));

    await Promise.all([
       rp('http://example.com/'), 
       rp('http://example.com/')
    ]);
}

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

Нам нужна проблема!

Конфликт отцов и сыновей

Война! Конфликт! Недоразумения! Кровь! Больше крови!

Напомним, что это Promise:

Promise будет принимать Resolve или Reject в качестве входных данных и возвращать Resolve или Reject в качестве выходных данных.

И совершенно обычным явлением было разрешить что-то со значением positive и reject с отрицательным. Лево и право. Светлый и темный. Вилки. Трубы.

Это просто контрольный поток. И вы можете это контролировать.

// if condition
if (midnight) {
    gotoSleep();
} else {
    workWorkWork();
}
// promise condition
Midnight
   .then(gotoSleep, workWorkWork);
// something more complex
someFormSubmission
   .then(validate)
   .then(send)
   .catch(showAHugeError); 
// or a bit simpler
someFormSubmission
   .then(loggingAsASideEffect)

У вас есть два потока данных на входе, вы предоставляете два потока на выходе. Вы можете разветвлять, разделять и присоединять потоки. Ты можешь делать что угодно…

Так в чем проблема?

Проблема довольно проста - одна (основная) особенность async / await - это выполнение кода по «прямой линии». Одна строка за другой.

Как результат - если вы чего-то ждете, вы получите результат этой асинхронной операции. Решимость. Следующий оператор после await - это разрешение.

Но где же Reject?

Пока нет возможности указать здесь путь отклонения - отклонение вызовет исключение.

try {
   const isMidnight = await Midnight; 
   // isMidnight always true.
} catch (err) {
   // isMidnight always NOT true. Even not undefined - out of scope
}

И это абсолютно нормально. Миллионы делают то же самое перед API-интерфейсами Promises и async - пытаются прочитать файл и перехватить исключение.

Просто молодое поколение похоже на дедов, а не на отцов.

Деды?

Отец обещания, и великий из async / await - это Callback, а его отец - The Synchronous. Кстати, это довольно странно:

  • Обещания используют обратный вызов во всех случаях.
  • Async / await использует промисы. И НЕ использовать обратные вызовы.
  • Но async / await ведут себя как прадеды.
  • И чтобы жить в мире с детьми, обещания должны вести себя как обратные вызовы.

Обещания надо усыновить, найти общий язык со своими детьми.

Итак, новый подход прост как камень:

Если есть результат - решайся с результатом.

Выбросить исключение в случае исключения.

И Ошибка не исключение. Это просто не очень хороший результат.

Например, POSIX API обычно возвращает «-1», если что-то пошло не так. А дальше вы можете проверить некоторые детали в переменной errno.

Если вы попытались открыть файл, но безуспешно - это не исключение. Это абсолютно нормальное и предсказуемое событие. Не найденный файл - это еще не конец света.

Исключение - непредвиденное событие.

Что дальше?

Есть еще одно довольно важное отличие.

Обещания - это цепочки небольших функций. Async / Await - одна большая функция. Итак, существует огромная разница в тестируемости, возможности повторного использования и разделения. Вы не можете добавить log в качестве побочного эффекта «без мыслей», как вы можете сделать это с помощью Promises, и вы не можете добавить какой-то шаг в середине выполнения так же «изначально», как вы можете сделать это с помощью Promises.

Вы все еще можете это сделать, но для этого потребуется другой подход.

И что?

Итак - в настоящее время некоторые библиотеки меняют свои API-интерфейсы на основе обещаний, чтобы они лучше соответствовали async / await.

Теперь они близки к API node.js - где обратный вызов использовался как для ошибки, так и для пути успеха. Или даже ближе к коду PHP.

$query = mysql_query("SELECT FROM BLA-BLA") or die(mysql_error())

Отцы в стремлении быть ближе к своим детям. Дети открывают корни.

Ничего страшного, но они меняют себя от самих себя. Впоследствии обещания становятся менее «обещаниями». Больше подходит для нового поколения, менее подходит для старой школы.

А это значит, что довольно сложно создать какую-то асинхронную библиотеку с хорошими интерфейсами как для старых интерфейсов на основе Promise, так и для новых асинхронных.

Это жизнь. Просто посмотрите на себя, а затем на своих родителей.