Обещания Javascript помогают разработчикам писать чистый код. Но как выглядит плохой JS-код…
Пока вы создаете серверные службы с помощью Node JS, важно знать, как Javascript изменил свой стиль кодирования, чтобы помочь разработчикам писать более качественный и чистый код. Вы не хотите, чтобы вас поймали за написанием кода в старом стиле среди ваших коллег 😃. Итак, давайте начнем наше путешествие со стилей кодирования Javascript и узнаем, почему были введены Promise
.
Во-первых, код, который удобен для разработчиков, но плох для JS.
Насколько простым будет код, если мы напишем его, как показано ниже:
(Воображаемый разговор между JS и разработчиком😃)
Разработчик: Это настолько интуитивно понятно, легко читается и понимается. Все инструкции идут одна за другой.
JS: Кричит вслух. ЭТО ПЛОХОЙ КОД.
Разработчик: Почему так?
JS: Я однопоточный и могу выполнять только одну задачу за раз. Каждый оператор блокирует поток до его завершения. Все они являются синхронными операторами.
Разработчик: Но первые три оператора зависят друг от друга. Они не могут выполняться, пока функция над ними не завершится.
JS: Согласен. Но почему someOtherFunctionNotDependentOnAbove();
нужно ждать завершения приведенного выше кода?
Разработчик: Да, но что мы можем сделать?
JS: Воспользуйтесь моим секретный ингредиент, обратные вызовы !!!, измените свой код на асинхронные операторы и разрешите обратным вызовам работать за кулисами.
Второй в очереди, стиль кода с обратным вызовом, хороший для JS, но сложный для чтения и написания разработчиками.
Следуя приведенному выше примеру, давайте напишем реальный код, полностью блокирующий или синхронный.
В приведенном выше коде есть все операторы блокировки, и, как мы видим, чтение 1000 файлов займет значительное время. Javascript никогда не поощряет этот код, а использует обратные вызовы, как показано ниже.
Мы сразу видим преимущества написания асинхронных вызовов. Каждый вызов функции является асинхронным, и зависимости между функциями обрабатываются обратными вызовами. Но у каждой медали есть две стороны. Обратные вызовы обеспечивают многозадачность в коде, но начинают создавать проблемы ад обратных вызовов или Пирамида судьбы. Если приведенный выше код выглядит не так уж плохо, посмотрите реальный пример на callbackhell.com.
Так что же хорошо и для разработчиков, и для JS? Входите, Обещания.
Обратные вызовы являются нормой для JS, но они создают проблему «ада обратных вызовов». Итак, JS решает эту проблему, используя класс Promise
. Функция, возвращающая Promise
, гарантирует, что вызываемая функция является асинхронной и не блокирует поток выполнения. Для начала давайте посмотрим, как определить функцию, которая возвращает Promise
.
Используя тот же пример для чтения каталогов, код с Promise
s теперь будет выглядеть так.
// First function which returns an Array of filenames as Promise function readDir() { return new Promise(function(resolve,reject) { fs.readdir('/pathToDirWith1000Files',function(err,fileList) { if(err) reject(err) else resolve(fileList) }) }) } // Second function that returns array of Promises from below func function readFiles(fileList) { var filePromises = fileList.map(function(fileName) { return readFile(fileName) }) return Promise.all(filePromises) } // Third function that returns Buffer as a Promise function readFile(fileName) { return new Promise(function(resolve,reject) { fs.readFile(fileName,function(err,buffer) { if(err) reject(err) else resolve(buffer) }) }) } // Main execution readDir() .then(fileList=>readFiles(fileList)) .then(buffer=>console.log(buffer)) .catch(err=>console.log(err))
Разве основная казнь не выглядит милой и простой? Но осталось немного доработать.
- Существует много шаблонного кода для создания функции, возвращающей Promise. Это можно исправить с помощью
async
функций. (Осторожно, в этом примере модульfs
нуждается вpromisify
util для работыasync
) - Используя
async
иawait
, мы можем вернуться к исходному стилю написания кода без потери преимуществ JS.
Окончательный стиль, код, который выглядит синхронно, но работает асинхронно
// Main execution var callAsyncCode = async function() { var fileList = await readDir() var filePromises = await readFiles(fileList) filePromises.map(buffer=>console.log(buffer)) }()
Таким образом, async
и await
вместе образуют синтаксический сахар для Promise
, с помощью которого разработчики могут писать синхронный код, но при этом достигать многозадачности, используя возможности обратных вызовов.
Резюме
Я надеюсь, что эта статья освежила воспоминания о кодировании в старом стиле для всех опытных JS-разработчиков, а также, надеюсь, предоставила некоторую предысторию для всех недавних разработчиков, надеющихся на JS….