Зоопарк функций: Часть 2 — Функция как отправленное сообщение

Часть1 — Часть 2 — Часть 3

Сегодня мы рассмотрим функции как способ отправки сообщений от одного объекта к другому. И в этой статье мы больше сосредоточимся на ожидаемых результатах выполнения функции.

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

Затем, имея это в виду, у нас может быть 3 типа сообщений, которые мы можем отправлять объектам:

  1. Ожидаются сообщения без ответа;
  2. Сообщения с немедленным ответом;
  3. Сообщения с отложенным ответом.

И на основе этих типов сообщений мы можем иметь соответствующие типы функций. На этом мы продолжим наше исследование функции zoo. Нумерация следует за предыдущей статьей

4. Процедура

«Рыбка, — сказала лягушка, — не могли бы вы приготовить чашечку вкусного чая, пожалуйста?»
«Иду», — ответила рыбка.

Процедура — это функция, которая не возвращает значение. Он вызывается для того, чтобы что-то выполнить. Это не чистая функция, она нужна для получения полезных побочных эффектов: распечатать документ, уведомить сообщением, отдать приказ роботу, сделать скриншот. С точки зрения сообщений это сообщение без ответа.

Насколько полезны процедуры? Что ж, они действительно могут быть очень полезными. Допустим, у вас есть умный дом, тогда вы можете сказать ему выключить свет. И обычно вы не хотите от него никаких ответов, вы просто хотите, чтобы он это сделал. И у него может быть какой-то API, и тогда вы можете вызвать его через JS следующим образом: SmartHome.turnOffTheLight() .

Это очень хорошо. Но на самом деле вы можете написать целое приложение, используя только процедуры. Если они просто знают, куда отправлять ответы, они могут функционировать как полнофункциональные функции (каламбур). Тогда код будет некрасивым, но он будет работать. Посмотрим на примере.

const Zoo = {
  // Procedure definition
  getAnimal(type, color, destination) {
    const animals = {
      Frog: {
        'Infra Red': 'ir_frog.jpg',
        'Caribbean Green': 'cr_frog.jpg',
      },
      Fish: {
        'Infra Red': 'ir_fish.jpg',
        'Caribbean Green': 'cr_fish.jpg',
      },
    };
    destination.receiveAnimal(animals[type][color]);
  },
};
const Receiver = {
  animals: [],
  execute() {
    // Procedure execution
    Zoo.getAnimal('Frog', 'Caribbean Green', this);
    Zoo.getAnimal('Fish', 'Infra Red', this);
  },
receiveAnimal(animal) {
    this.animals.push(animal);
  },
};
Receiver.execute();
console.log(Receiver.animals);
// => [ 'cr_frog.jpg', 'ir_fish.jpg' ]

Как видите, вы все еще можете делать довольно мощные вещи с помощью процедур. На самом деле этот код очень напоминает Visitor Pattern. Также в этом коде вы можете вызывать destination.receiveAnimal несколько раз. Таким образом, это очень похоже на реальный разговор в мессенджерах. И из этого примера вы можете почувствовать, что видите функции как сообщения между объектами.

Основная функция

«Рыба, — сказала лягушка, — какая твоя любимая картина?»
«Композиция VIII Кандинского», — ответила рыбка.

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

function getAnimal(type, color) {
  const animals = {
    Frog: {
      'Infra Red': 'ir_frog.jpg',
      'Caribbean Green': 'cr_frog.jpg',
    },
    Fish: {
      'Infra Red': 'ir_fish.jpg',
      'Caribbean Green': 'cr_fish.jpg',
    },
  };
  return animals[type][color];
}
console.log(getAnimal('Frog', 'Caribbean Green'));
// => 'cr_frog.jpg'

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

5. Асинхронная функция

«Рыбка, — сказала лягушка, — какой твой любимый композитор?»
«Мой любимый композитор — Людовико Эйнауди», — сказала рыбка после нескольких минут раздумий.

Как вы уже догадались, асинхронная функция — следующая в нашем списке. Это сообщение с отложенным ответом. Однажды спросив, вы не ждете ответа. И ответ может прийти в любое время. Это как отправить электронное письмо. Может быть, ответа и не будет. Хотя на самом деле это не ожидается в коде.

function later(value) {
  return new Promise((resolve, reject) =>
    setTimeout(() => resolve(value), 1000),
  );
}
async function getAnimal(type, color) {
  const animals = {
    Frog: {
      'Infra Red': 'ir_frog.jpg',
      'Caribbean Green': 'cr_frog.jpg',
    },
    Fish: {
      'Infra Red': 'ir_fish.jpg',
      'Caribbean Green': 'cr_fish.jpg',
    },
  };
  return await later(animals[type][color]);
}
async function receiveAnimal() {
  console.log(await getAnimal('Frog', 'Caribbean Green'));
  // => 'cr_frog.jpg' (after 1s)
  console.log(await getAnimal('Fish', 'Infra Red'));
  // => 'ir_fish.jpg' (after 1s)
}
receiveAnimal();

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

Хотя это свойство является ограничивающим, оно также очень удобно и приводит к более чистому и красивому коду.

6. Функция с обратным вызовом

«Рыбка, — сказала лягушка, — какой твой любимый фильм?»
«Олдбой». сказала рыба: «Нет, подожди! Это револьвер. Это определенно револьвер, — повторила рыба.

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

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

Просто взгляните на этот пример, чтобы доказать эти два момента:

import { otherReceiver } from './otherZoo';
function later(value, cb) {
  setTimeout(() => cb(value), 1000);
}
function getAnimal(type, color, cb) {
  const animals = {
    Frog: {
      'Infra Red': 'ir_frog.jpg',
      'Caribbean Green': 'cr_frog.jpg',
    },
    Fish: {
      'Infra Red': 'ir_fish.jpg',
      'Caribbean Green': 'cr_fish.jpg',
    },
  };
  later(animals[type][color], cb);
  return 'immediate return value';
}
// Use return value in the same place
console.log(
  getAnimal(
    'Frog', 
    'Caribbean Green', 
    value => console.log(value)
  )
);
// => 'immediate return value'
// => 'cr_frog.jpg' (after 1s)
// send return value to another place
getAnimal('Frog', 'Caribbean Green', otherReceiver);

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

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

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

Желаю вам красивых сеансов кодирования! █