Фон

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

Для людей вроде меня, которые только что закончили факультет CS или все еще учатся; иметь некоторое умеренное понимание объектно-ориентированного подхода, но не иметь знаний в области веб-технологий или подобного JavaScript; просто смотреть на такие коды кажется ошеломляющим. На ум приходит только одно: это вообще логично?

Несколько дней назад я сказал, что достаточно, и начал копаться в nodejs, чтобы попытаться найти в этом смысл. Мне помогли некоторые из моих друзей, которые их знают, и, изучив код, я наткнулся на термин «обещание».

Привет! Я слышал это раньше !!

… Останавливая их на середине их объяснения. Теперь у меня есть некоторый опыт разработки под Android, и несколько раз мне приходилось использовать класс под названием AsyncTask. Если вам это неизвестно, вы можете изучить документацию, в которой это действительно хорошо объясняется. Итак, это так называемое обещание казалось похожим на AsyncTask, и я немного погуглил, чтобы узнать это получше.

Предварительное условие: лямбда в java

Я сделал 4 или 5 проектов для Android, и все они имели по крайней мере одну общую черту: Кнопки.

Клише в андроид-проектах. Любите это, ненавидьте, как хотите; но от этого никуда не деться. Каждый раз, когда вы помещаете кнопку в приложение, появляется setOnClickListener.

Теперь метод setOnClickListener класса Button принимает в качестве параметра один interface: OnClickListener интерфейс класса View.

Имейте в виду, что это интерфейс с одним методом. Есть только один метод:

public interface OnClickListener {
    void onClick(View v);
}

определение OnClickListener. Реализация следующая:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(MainActivity.this,
            "Hey! you clicked me!!",Toast.LENGTH_LONG).show();
    }
});

Очень простой. Имеет смысл, правда?

Во время моего последнего проекта я с любопытством обновил версию java до 8, и все эти простые на вид реализации OnClickListener начали выдавать предупреждения!

Он попросил меня преобразовать реализацию интерфейса в лямбду.

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

Вот об этом очень хорошая статья. Я не буду больше это объяснять.

Вернемся к истории

Я искал статьи, которые более подробно объясняют это обещание, и наткнулся на эту статью. Здесь прекрасно объясняется, что делает обещание и как оно работает с использованием этого кода:

Именно поток данных этих двух строк действительно сбил меня с толку. Я прекрасно понял статью; о том, как это работает; но я не смог бы сделать это самостоятельно, если бы дал другую задачу, касающуюся «обещания».

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

Преобразование лямбд

Из двух приведенных выше строк любой может определить 3 большие лямбды, следуя знакам = ›.

Давайте сначала возьмем последний:

() = ›console.log (« Здравствуйте! »)

и преобразовать его в интерфейс java. Единственный метод результирующего интерфейса не будет иметь никаких параметров. так это выглядит так:

а затем внешняя лямбда первой строки:

time = ›новое обещание ( (разрешение) =› setTimeout (разрешение, время) )

Требуется всего один параметр (время). И мы можем догадаться по названию параметра, что тип этого параметра будет длинным.

Эта лямбда присваивается переменной с именем wait. ждать позже выполняется как «обещание»; поэтому мы можем сказать, что эта лямбда возвращает обещание (еще один класс, который нам придется создать).

а теперь внутренняя лямбда:

(разрешение) = ›setTimeout (разрешение, время)

теперь это сложно. Он используется внутри конструктора класса Promise. Итак, параметр resolve, который он принимает; исходит из строящегося обещания.

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

Но как он передаст сообщение до самого обещания? Должен ли метод возвращать сообщение? Нет ... кому-то придется сидеть все время, чтобы setTimeout закончился и получил результат. Нет смысла запускать это в фоновом потоке. Потоку переднего плана все равно придется ждать.

Что мы делаем?

Ситуация такая; нам действительно нужен обратный вызов = ›интерфейс =› лямбда

Мы можем наблюдать это снова

Подождите минуту! Есть еще один параметр, который все это время игнорировался! О да. Параметр resolve. Это может быть обратный вызов нашего спасителя. До сих пор мы рассматривали параметры как некоторые переменные, такие как объекты, но они также могут быть лямбдами! И этот приятель resolve на самом деле является лямбда-выражением. Поскольку он используется только для уведомления обещания о состоянии задачи, это может быть так просто:

И вернемся к третьей лямбде:

(разрешение) = ›setTimeout (разрешение, время)

Теперь мы можем быть уверены, что соответствующий интерфейс будет выглядеть так:

Теперь, когда мы преобразовали все лямбды в интерфейсы, давайте посмотрим, как будет выглядеть полная реализация:

‹Примечание. Я отказался от механизма потоковой передачи, чтобы сохранить это только в отношении потока данных. Реализация исходного обещания будет во многом отличаться ›

Во время вызова конструктора обещание сохранит только ResloverInterface, чтобы использовать его позже. Когда вызывается метод then, он сохраняет данный ThenCallback и вызывает сохраненный ResolverInterface; и тогда начинается настоящая работа. После завершения работы вызывается PromiseStateInterface с именем resolve, и он вызывает сохраненный ThenCallback. ThenCallback выполняет задачу, указанную для выполнения поста (в данном случае вывод «Hello!»).

ссылка на проекты на github: https://github.com/mainstring/PromiseLearningInJava

Этот пример охватывает только случай Promise resolve. С reject будет сложнее, но концепция потока данных останется почти такой же.

Я сам новичок в этой культуре JavaScript. Так что, в случае каких-либо несоответствий, пожалуйста, дайте мне знать.