Фон
Мир кишит кодами, написанными с помощью функционального программирования. Куда бы мы ни посмотрели, любая современная технология, с которой мы взаимодействуем, построена функционально. Заглянув ниже, мы видим множество лямбда-функций; сидят там, назначаются, зовут друг друга, повторяются, крутятся, меняют формы, меняют использование, меняют всю свою цель по своему желанию; выглядит как злой змей, созданный самым темным колдовством.
Для людей вроде меня, которые только что закончили факультет 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. Так что, в случае каких-либо несоответствий, пожалуйста, дайте мне знать.