Async / await - это новый синтаксис, поставляемый с es7, который позволяет синхронно писать асинхронный код. В приведенном ниже примере показано, как новый синтаксис сравнивается с простым использованием обещаний.

// with async fns
// Note: we are using a promisified version of request and readFile
async function fetchAndReadAsync(url, dir) {
  var data = await requestPromise(url);
  var fromFiles = await readFilePromise(dir, ‘utf-8’)
  return [data.body, fromFiles];
}
async function run() {
  var fromAsyncFunctions = await requestAndWriteAsync(“/foo.txt”,       “https://www.foo.com/bar")
  return valsFromAsyncFunctions;
}
run()
  .then(success => console.log(success))
  .catch(err => console.log(err.message, err.stack));
// with promises
function fetchAndReadPromise (url, dir) {
  var dataBody;
  return requestPromise(url).then( ( data )=> {
      dataBody = data.body;
      return readFilePromise(dir, ‘utf-8’);
  }).then( ( fromFiles ) => {
    return [dataBody, fromFiles];
  })
}
function run() {
  var fromPromises;
  return run(“/foo.txt”, “https://www.foo.com/bar").then((data) => {
    fromPromises = data;
    return fromPromises;
  })
}
run()
  .then(success => console.log(success))
  .catch(err => console.log(err.message, err.stack));;

Некоторые могут возразить, что синтаксис async / await устраняет необходимость понимания обещаний, что в некоторой степени верно, поскольку он выполняет достойную работу по абстрагированию реализации Promise.

Однако я считаю, что понимание Promises позволяет использовать мощную форму асинхронного функционального программирования с async / await, которую вы можете получить только с помощью Javascript Promises.

Удивительный подъемный механизм с async / await

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

var request = require('request');
var requestPromise = util.promisify(request);
async function fetchAsync(url) {
  var data = await requestPromise(url);
  return data;
}
var pendingPromise = fetchAsync(‘https://foo.com');
console.log(pendingPromise) // Promise<…Pending>

Возвращаемое значение async fn не является возвращаемой переменной «data», а «ожидающим обещанием», которое после разрешения передаст переменную «data» в свойство функции «then». Таким образом, мы могли получить доступ к таким данным.

fetchAsync(‘https://foo.com')
  .then(function(data) {
    console.log(data) // {body: [object Object]}
  })

Ключевым моментом здесь является то, что fetchAsync возвращает ожидающее обещание, что означает, что возвращаемое значение функции fetchAsync также может ожидаться.

async function run() {
  var data = await fetchAsync(‘https://foo.com');
  return data;
}

Это позволяет нам использовать парадигму FP, называемую «лифтинг». Лифтинг - это преобразование данных из одной формы в другую. Например, Array.prototype.map берет массив и переводит его в другую форму массива. (Интересный факт функционального программирования: карта определяется как функция, выполняющая подъем)

Исправление: как упоминалось в комментариях, Array.prototype.map не поднимает один массив на другой, а, скорее, поднимает функцию, которая предназначена для того, чтобы одна запись могла быть применена к массиву этой записи.

Вот пример подъема с использованием нашей функции «fetchAsync» сверху.

var request = require('request');
var fs = require('fs');
var requestPromise = util.promisify(request);
var writeFilePromise = util.promisify(fs.writeFile);
// mapToWriteFile will lift our pending fetch promise to a 
// pending fetch and write promise
async mapToWriteFile(pendingFetch, dir) {
  var data = await pendingFetch;
  var written = await writeFilePromise(data, dir);
  return written;
}
async function run() {
  var pendingFetch = fetchAsync('https://foo.com');
  var pendingFetchAndWrite =     
    mapToWriteFile(pendingFetch, '/foo.txt');
  var success = await pendingFetchAndWrite;
  // fs.writeFile does not pass a value into it’s ‘then’ function 
  // property, but in case that it did the return value would be 
  // thenable with the success message.
  return success;
}
run()
  .then(success=> console.log(success))
  .catch(err => console.log(err.message, err.stack));

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

Это довольно хорошо интегрируется в систему функционального программирования Javascript, как мы видим на более сложном примере.

import urls from ‘./modules/urls’; // array of urls
// the fetchAsync function is taken from the previous example
async function run() {
  var pendingFetches = urls.map(fetchAsync);
  var pendingWrites = pendingFetches.map( pendingFetch =>   
    mapToWriteFile(pendingFetch, ‘/foo.txt’)
  );
  var success = await Promise.all(pendingWrites);
  return success;
}
run()
  .then(success => console.log(success))
  .catch(err => console.log(err.message, err.stack));

Удивительная гарантия неизменности

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

Это означает, что асинхронная функция не может гарантировать отсутствие побочных эффектов в текущем стеке вызовов. Например:

async function run( mutableData ) {
  var data = await fetchAsync(‘https://foo.com');
  //this change doesn’t go into effect in the current call stack
  mutableData.prop = data.body;
}
run();

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

Предостережение: код, написанный над первым ключевым словом «await», выполняется синхронно, поэтому гарантия действует до тех пор, пока кто-то избегает мутации ввода над первым ключевым словом «await» (что было бы необычно).

До сих пор это был мой опыт работы с async / await, есть другие утилиты функционального программирования, которые я не исследовал, такие как каррирование и композиция функций. Но до следующего раза ...