Как «Отцы и дети» связаны с функцией 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, так и для новых асинхронных.
Это жизнь. Просто посмотрите на себя, а затем на своих родителей.