В поисках Грааля каждые 16 минут на Земле ...
От: c-smile Канада http://terrainformatica.com
Дата: 09.03.15 01:28
Оценка: +3 :))) :)))
... рождается котёнок JavaScript framework.

Исследования показали что Todo List это самое сложное веб приложение что вы можете написать перед тем как новый, еще лучший framework будет изобретен...

TodoMVC сайт на настоящий момент сожержит 63 (!) имплементаций Todo List webapp с использованием разных frameworks (помимо vanilla JS и jQuery).

...

И вот казалось Грааль найден


но! Angular команда решила что Angular2 не будет совместим с Angular1.

Йиии-хх-аа, эта пестня хороша — начинай сначала

Весь текст тут http://www.allenpike.com/2015/javascript-framework-fatigue/
Re[4]: В поисках Грааля каждые 16 минут на Земле ...
От: maxkar  
Дата: 15.03.15 11:30
Оценка: 22 (4)
Здравствуйте, c-smile, Вы писали:

M>>Я свой идеальный API уже знаю, умею писать и потихоньку алгоритмическую базу обновляю. Называется реактивное программирование и выглядит так:

CS>Как-то это похоже на knockout.js , нет ?

Да, похоже. Многие API будут похожи друг на друга. В конце концов, behaviour/observable — это монада. Там всего лишь три элемента: тип, return и bind. Для реактивного программирования добавляется variable. Поэтому если принято решение делать архитектуру на "маленьких observable (т.е. как в knockout том же, каждое свойство observable вместо их контейнера), она будет в чем-то похожа на knockout.

А вот в деталях (очень важных деталях) мой API отличается от knockout. Это скорее flapjax. В свое время я за основу как раз брал flapjax, но его не было под нужную платформу. Поэтому API (и структура UI) сделана по его мотивам, а реализация уже своя.

Теперь покажу те самые важные детали:
function blackMagic(x, y, z) {
  return x + y *z;
}

//Knockout:
var x = ko.observable(5);
var y = ko.observable(7);
var z1 = ko.computed(function() {
  return blackMagic(x.value(), 42, y.value());
});
var z2 = ko.computed(function() {
  return blackMagic(4, x.value(), x.value());
});

//Mine:
var x = R.variable(5);
var y = R.variable(7);
var z1 = R.apply(blackMagic, [x, 42, y]);
var z2 = R.apply(blackMagic, [4, x, x]);


У меня меньше boilerplate, что уже само по себе хорошо. Но важнее то, что "мой" API заставляет разработчиков писать чистые функции а не "привязанный к модели computed". Эти функции можно использовать в разных контекстах, проще тестировать.

Вторая мелочь — трекинг зависимостей и количество обновлений:
function f1(x, y) {
  return 2 * x + y;
}
function f2(x, y) {
  return x - y + 4;
}
var a = R.variable(1);
var b = R.apply(f1, [a, 3]);
var c = R.apply(f2, [a, 9]);
var res = R.apply(f1, [b, c]);
R.apply(alert, [res]); 
// Сколько будет алертов при вычислении следующей строки:
a.set(10);

В моей реализации будет один alert с новым значением. В knockout и почти всех других — скорее всего два (из-за двух путей к источнику изменений). Во многих случаях это не важно, но у меня в реальной задаче где-то на вершине графа висела текстура, а внизу менялись два входа (variable) одновременно. И разница между одним и двумя пересчетами была заметна. Так что теперь я умею перевычислять значения "точно"

Третья мелочь. Управление временем жизни моделей. Обычно оно делается через weak references. Я уже и не помню, почему я не стал делать так. Либо меня смущало, что на вершинах там живет DOM (и могут быть какие-то расходы по быстродействию до запуска GC). Либо просто было изобретено не мной. Поэтому управление временем жизни приняло такую форму:
// Какие-то глобальные (или долгоживущие) observable.
var globalA, globalB;

// Временный scope (например, диалоговое окно)
var session = R.session();
var x = R.variable(123);
var a = session.proxy(globalA);
var b = session.proxy(globalB);
var y = R.apply(fn, [a, b, x]);
//...
// При закрытии окна
session.destroy();

У меня при необходимости вся временная модель "отрезается" от родительской за один вызов и по линии отреза.

Итого. Да, с одной стороны, похоже. Но с другой стороны, есть и отличия, которые хорошо проявляются только на больших проектах. На hello, world разницы особой нет.

P.S. Есть и аналог knockout.js в мире скала. Это scala.rx. Там как раз "интрузивные" применения функций и сложные вычисления.
Re[2]: В поисках Грааля каждые 16 минут на Земле ...
От: c-smile Канада http://terrainformatica.com
Дата: 09.03.15 19:49
Оценка: +3 :)
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, c-smile, Вы писали:


CS>>но! Angular команда решила что Angular2 не будет совместим с Angular1.

G>Он и между минорными релизами был не очень-то совместим.

У меня такое впечатление что основная толпа "руби-роидов" перебежала с RoR на Angular.
Во всяком случае такие же шумные и энтузиастнутые ...

А вообще количество TodoMVC конечно зашкаливает.

Проще написать именно то что надо здесь и сейчас на jQuery чем найти 1 зерно в 63 плевелах.
И вот когда ты это своё написал, по науке, с абстракциями и всеми делами то тут же рогатый тебе и шепчет "Смотри как классно! Давай выкладывай это дело на TodoMVC — осчастливь человечество".
Re[2]: В поисках Грааля каждые 16 минут на Земле ...
От: c-smile Канада http://terrainformatica.com
Дата: 09.03.15 03:48
Оценка: 20 (3)
Здравствуйте, vsb, Вы писали:

vsb>А MVC хорошего как не было, так и нет нигде


А что такое хороший MVC?

Если "хороший" это "тот который есть не просит" то вот 60 lines of code от меня https://github.com/c-smile/spapp
Re[2]: В поисках Грааля каждые 16 минут на Земле ...
От: maxkar  
Дата: 09.03.15 09:35
Оценка: 2 (1)
Здравствуйте, vsb, Вы писали:

vsb>А MVC хорошего как не было, так и нет нигде


А самому сделать? Я свой идеальный API уже знаю, умею писать и потихоньку алгоритмическую базу обновляю. Называется реактивное программирование и выглядит так:
var x = Behavior.variable(3);
var y = Behavior.variable(5);

function calc(x, y, z) {
  return x + y * z;
}

var res = Behavior.apply(calc, [x, y, 7]);
var elt = UI.div(UI.text(res));
Behavior.appply(alert, res);

x.set(10);
y.set(20);


При вызове set все нужные функции перевычисляются. (т.е. в примере будет два alert'а). В отличие от того же ангуляра, прекрасно работает с массивами и позовляет писать что-то вроде

function filter(spec, items) {
  var res = [];
  items.foreach(function(item) {
    if (item.firstName == spec.firstName && item.lastName == spec.lastName)
      res.append(item);
  });
  return res;
}

function mkSpec(firstName, lastName) {
  return { firstName : firstName, lastName : lastName };
}

var firstName = ...
var lastName = ...
var allItems = ...

var itemsToShow = Behavior.apply(filter, [Behavior.apply(mkSpec, [firstName, lastName]), allItems]);
var itemPages = Behavior.apply(paginate, [itemsToShow]);
var pageToDisplay = Behavior.apply(Arrays.itemAt, [itemPages, currentPage]);


Нет никаких "циклов перевычислений" (с ангуляром — беда на массивах). В отличие от того же react.js, оно гвоздями не прибито к DOM (у меня вообще подозрение, что react.js тоже все перевычисляет, иначе virtual DOM просто не нужен). API чистый и удобный. Во многих местах можно писать чистые функции и потом биндить их к модели (а чистые функции хорошо тестировать).

Всякие мелочи удобно реализуются. Тот же человекообразный Async на Behavior (вместо страшных promise в CPS будет Async.apply(fn, args...)). Желающие могут посмотреть реализацию async у меня.

Я на эту тему могу говорить еще долго, но будет ли кто-то слушать? Именно на js на библиотеку посмотреть нельзя. Нету ее пока, у меня нет задач, где она нужна (плюс я не уверен, что single-page app так часто нужны, для чего-нибудь простого API от c-smile выглядит хорошо). Но пишется меньше, чем за неделю. Версию на scala можно найти в репозитории по ссылке выше (планируются существенные улучшения в математике без изменения API), там же полностью рабочий десктопный пример. Могу и под достойную задачу на js написать версию. Или можем обсудить и я могу присоединиться к вашей команде и показать, как и что. У меня задачи достойные для reactive programming слишком быстро кончаются, нужны адоптеры
Re[3]: В поисках Грааля каждые 16 минут на Земле ...
От: vsb Казахстан  
Дата: 09.03.15 14:57
Оценка: +1
Здравствуйте, c-smile, Вы писали:

vsb>>А MVC хорошего как не было, так и нет нигде


CS>А что такое хороший MVC?


Это где есть model, view, controller и стрелочки стоят в нужном направлении, как в smalltalk-е было. Всё остальное это не MVC (не обязательно хуже, но называться MVC не должно).
Re[7]: В поисках Грааля каждые 16 минут на Земле ...
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 24.03.15 09:50
Оценка: +1
Здравствуйте, maxkar, Вы писали:


I>>Ужос. У тебя весь флоу раскидан непойми по чем. В любом месте самое главное находится где то еще.

M>Это где это? Все идет так же последовательно. Ну ровно так же как в твоем примере с yield (и переписывается он туда механически). А group обычным программистам знать не нужно, это детали реализаций. Точно так же, как и детали реализации promise.

Я не вижу никакого "ровно так же". У тебя весь флоу, т.е. конкретная цепочка задается в функции. Последовательное выполнение это вобщем редкость. Часто бывают ветвления, итерации и тд и тд и тд. Вот, скажем, аджакс запрос может сфейлиться трижды. Опаньки — добавляем изменения в сам флоу. Потом, оказывается, ошибка должна браться не последняя, а самая первая — снова изменения в сам флоу. Как это у тебя будет — не ясно.

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

M>Да нет, не особо. Как-то так получается, что почти все нужные функции находятся в одном файле. А то, что находится в другом, обычно уже работает.

А хочется все бряки поставить ровно в одной функции.


I>>А это уже не интересно. На конкретной задаче вышло все не так удобно, как ты говорил. Отмена и прогресс это очень трудоемкие в отладке задачи, здесь хочется иметь максимум контроля.

M>А как же твой пример с Cancel? Где там максимум контроля?

А что не так ? Все что нужно — доставить колбек отмены до всех точек вниз по цепочке вызовов.

>Где parent.cancel, например? Еще у меня есть претензии к parentProgress. Вот нет у меня такого и быть не может. Потому, что, например, метод getDepartmentState(deptId) может быть вызван из нескольких мест. И возвращает этот метод один и тот же промис для всех (ибо незачем одно и то же грузить 10 раз). Что там будет с progress? И не надо мне предлагать "все-таки запустить операцию два раза". Не хочу я под ограничения вашей технологии плясать.


Если тебе прогресс не нужен, значит у тебя другой кейс. Если твоя либа удобна только для твоих кейсов, так и пиши.

M>Ты просто берешь некоторый "уже готовый" API и говоришь, что "вот как все просто с ним на задачах, подходящих для него". Это да, хорошо, если оно подходит под задачу. А если нет? Я не буду свои хотелки под API подгонять, я буду API подгонять под хотелки. Так что важен именно вопрос "хотелок". Реализация под них — дело чисто механическое.


Я не брал никакой специальный апи. Я взял самый базовый уровень — аджакс, промисы и тд, то есть. Вот например в Cordova/Phonegap добавляется файловое апи и кое какое другое, все ровно то же — минимум приседаний.

I>>Ну вот у тебя и вышло такой вот неомодернизм, красивый в теории, но неудобный на практике.

M>Пользовался. Удобный. Мне удобнее аппликативный вид чем промисы. Команда потихоньку к этому же склоняется

Если команда 'потихоньку склоняется', значит дело не в удобстве а в чем то еще.

M>Ну и? Проблема в чем? Мы оцениваем вроде бы объем библиотечного кода. Это точно "библиотечные" 10мб? Какая разница, будут они использовать promise или мой async? И вдруг на моем async там всего 8мб получится?


А почему вдруг именно библиотечный код надо оценивать ? Вот его объем как раз не важен.

I>>Если ты в отрыве от команды, то запросто. А если есть команда, то её нужно научить всем твоим приседаниям.


M>Ну как сказать. Команду я научу за 2 часа реактивному программированию (это даже много, я презенташки проводил уже).


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

I>>И вот реально проще взять обычные промисы, которые уже нативные чуть не всюду, и спокойно писать асинхронный код.

I>>А в следующей версии JS можно будет убрать цепочки then в yield:
M>Ну посмотрел.

M>Ну и какая принципиальная разница с yield? Ну чуть-чуть по-другому пишется. Async.apply используется вместо yield. Но какой-то концептуальной разницы я не вижу. Причем Aysnc работает уже сейчас и не нужно ждать, пока достаточно пользователей переедет на браузеры, которые поддерживают promise.


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

M>А что будет, если нужно выполнить несколько асинхронных действий параллельно. Причем с недетерминированным порядком ожидания. Т.е. "кто раньше готов исполняться, тот и выполняется".


M>В зависимости от порядка завершения op1/op3 будет изменяться и порядок запуска doAsyncGood/doAsyncEvil. Это читаемо на yield/then будет? Обрати внимание, что случай одного/многих аргументов ничем не отличается. И для функций, возвращающих promise и возвращающих значение разница тоже не большая. И, кстати, что там с progress будет в этом сценарии?


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

I>>Здесь кроме промисов совсем тонкий апи для отмены, прогреса и аджакса, самый мизер, прозрачнее некуда, бряки ставь куда хочешь прямо здесь.

M>А вот у меня про твой пример вопрос. sha1.get — это синхронная или асинхронная функция? И если синхронная, то как ее последний аргумент обрабатывается? Обрабатывается ли он yield или это все внутри функции происходит и она обязана за "weight" следить? И каждая асинхронная функция тоже обязана за переданным weight следить? Как-то накладно получается.

Асинхронная разумеется. Следить ни за чем не надо, createProgress создаёт список из прогрессов, сиречь колбеков.

M>С бряками да, нужно ставить на функции. Но я ведь могу поменять Async.apply(smth, [...]) на Async.apply(function() { return smth.apply(null, arguments); }, [...]) и ставить бряки в ней. Но на моей практике это если и было нужно, то очень редко. А параллельное (менее детерминированное) выполнение в последовательном виде все равно не пишется (а если пишется, там все те же проблемы с брекпоинтами и недетерминированностью).


У тебя все равно получаются косвенные вызовы на ровном месте.

M>В общем, у меня Async API тоже тонкая прослойка над reactive.


Которая, в свою очередь, очень толстая обертка вокруг колбеков-эвентов.

>Только ты давай сначала показывай желаемый API или желаемые задачи. Потому что без этого дискуссия будет неконструктивная.


Я уже показал, вполне достаточно

M>P.S. Я не говорю, что Promises совсем отстой. Они удобны в некоторых классах задач. Только вопрос в том, что же это за классы. И второй вопрос: что делать, когда реальная задача не попадает ни в один из них?


Я сильно сомневаюсь, что реактивщина может решать более широкий класс задач. Издержки просто конские. Более того — даже в менее ресурсоемких платформах, навроде дотнета или джавы, реактивщина все равно распыляет ресурсы за просто так.
Re: В поисках Грааля каждые 16 минут на Земле ...
От: vsb Казахстан  
Дата: 09.03.15 02:45
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Йиии-хх-аа, эта пестня хороша — начинай сначала


Сейчас react.js модно.

А MVC хорошего как не было, так и нет нигде
Отредактировано 09.03.2015 2:46 vsb . Предыдущая версия .
Re: В поисках Грааля каждые 16 минут на Земле ...
От: Mamut Швеция http://dmitriid.com
Дата: 09.03.15 08:33
Оценка:
CS>но! Angular команда решила что Angular2 не будет совместим с Angular1.

Про это на твиттере кто-то правильно сказал: Angular продвигается Гуглом, и Гугл использует его в 0 своих проектах. React.js продвигается Facebook'ом, и используется в его реальных проектах.


dmitriid.comGitHubLinkedIn
Re[2]: В поисках Грааля каждые 16 минут на Земле ...
От: Sharov Россия  
Дата: 09.03.15 09:59
Оценка:
Здравствуйте, Mamut, Вы писали:

M>Про это на твиттере кто-то правильно сказал: Angular продвигается Гуглом, и Гугл использует его в 0 своих проектах.


В гмейле вроде бы используется. Разработчики Angular из этой группы.
Кодом людям нужно помогать!
Re[3]: В поисках Грааля каждые 16 минут на Земле ...
От: maxkar  
Дата: 09.03.15 11:02
Оценка:
Здравствуйте, Sharov, Вы писали:

S>В гмейле вроде бы используется. Разработчики Angular из этой группы.


Не получилось найти ангулара. Ни в network browser, ни в разметке. Коллективный разум говорит, что они используют Closure Tools.
Re: В поисках Грааля каждые 16 минут на Земле ...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 09.03.15 14:39
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>но! Angular команда решила что Angular2 не будет совместим с Angular1.

Он и между минорными релизами был не очень-то совместим.
Re[3]: В поисках Грааля каждые 16 минут на Земле ...
От: vsb Казахстан  
Дата: 09.03.15 14:58
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Здравствуйте, vsb, Вы писали:


vsb>>А MVC хорошего как не было, так и нет нигде


M>А самому сделать?


Периодически хочу, но лень побеждает. Да и не пишу пока больших JS проектов, где это реально надо, лапшекода хватает.
Re[3]: В поисках Грааля каждые 16 минут на Земле ...
От: c-smile Канада http://terrainformatica.com
Дата: 09.03.15 18:58
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Я свой идеальный API уже знаю, умею писать и потихоньку алгоритмическую базу обновляю. Называется реактивное программирование и выглядит так:


Как-то это похоже на knockout.js , нет ?
Re[4]: В поисках Грааля каждые 16 минут на Земле ...
От: Sharov Россия  
Дата: 09.03.15 19:55
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Здравствуйте, Sharov, Вы писали:


S>>В гмейле вроде бы используется. Разработчики Angular из этой группы.


M>Не получилось найти ангулара. Ни в network browser, ни в разметке. Коллективный разум говорит, что они используют Closure Tools.


Да, это я похоже ошибся -- wishful thinking. Но можно погуглить "where does google use angularjs", и среди прочих будут действительно
разработки гугла на Angular. Наример, YouTube on PS3.
Кодом людям нужно помогать!
Re[3]: В поисках Грааля каждые 16 минут на Земле ...
От: Sharov Россия  
Дата: 10.03.15 10:28
Оценка:
Здравствуйте, Sharov, Вы писали:

Вот еще можно посмотреть крупные продукты, в которых используется Angular.
Кодом людям нужно помогать!
Re[2]: В поисках Грааля каждые 16 минут на Земле ...
От: dalmal  
Дата: 15.03.15 13:34
Оценка:
Здравствуйте, Mamut, Вы писали:

M>Про это на твиттере кто-то правильно сказал: Angular продвигается Гуглом, и Гугл использует его в 0 своих проектах.

Вроде ж используется, просто во внутренних проектах, не? Всякая там автоматизация тыры-пыры.

M>React.js продвигается Facebook'ом, и используется в его реальных проектах.

Это разные фреймворки для разных вещей.
Re[3]: В поисках Грааля каждые 16 минут на Земле ...
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 15.03.15 20:31
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Всякие мелочи удобно реализуются. Тот же человекообразный Async на Behavior (вместо страшных promise в CPS будет Async.apply(fn, args...)). Желающие могут посмотреть реализацию async у меня.


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

M>Я на эту тему могу говорить еще долго, но будет ли кто-то слушать? Именно на js на библиотеку посмотреть нельзя.

...
> Но пишется меньше, чем за неделю.

Это сказки. Чтото реактивное можно и за неделю. А чтото концептуальное, годное для продакшна в JS, пишется долго и мучительно. За примерами см например bacon.js и подобные либы.
Re[4]: В поисках Грааля каждые 16 минут на Земле ...
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 15.03.15 20:33
Оценка:
Здравствуйте, vsb, Вы писали:

M>>А самому сделать?


vsb>Периодически хочу, но лень побеждает. Да и не пишу пока больших JS проектов, где это реально надо, лапшекода хватает.


Вся реактивщина работает на порядок медленнее тех же промисов. И если промисы можно выпрямить в линейный код с помощью yield, лифтинга и тд, то реактивщину вспотеешь спрямлять.
Re[5]: В поисках Грааля каждые 16 минут на Земле ...
От: c-smile Канада http://terrainformatica.com
Дата: 15.03.15 20:39
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Здравствуйте, c-smile, Вы писали:


M>//Mine:

M>var x = R.variable(5);
M>var y = R.variable(7);
M>var z1 = R.apply(blackMagic, [x, 42, y]);
M>var z2 = R.apply(blackMagic, [4, x, x]);
M>[/code]

Я для Sciter написал был что-то такое reactive/data-bound по имени +plus ( {sciter-sdk}/samples/+plus )

script:

namespace KindOfModel {
  var x = 5;
  var y = 7;
  var z1,z2;
  
  @observing "x" "y"  
    function() { z1 = x + y + 42; }

  @observing "x" 
    function() { z2 = 2 * x + 42; }
}


делает то же самое что и у тебя как я понимаю. Только кроме того еще binding с UI:

<form model="KindOfModel">
  <label>X</label> <input(x)>
  <label>Y</label> <input(y)>
  Z1: <output(z1)> and Z2 <output(z2)>
</form>


Без binding c UI я так понимаю это всё смысла не имеет.
Хотя если мне не изменяет память вроде как Prolog позволял деклартивно вычислительные сети рисовать. Но могу ошибаться, давно это было.
Re[4]: В поисках Грааля каждые 16 минут на Земле ...
От: maxkar  
Дата: 18.03.15 22:49
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Здравствуйте, maxkar, Вы писали:


M>>Всякие мелочи удобно реализуются. Тот же человекообразный Async на Behavior (вместо страшных promise в CPS будет Async.apply(fn, args...)). Желающие могут посмотреть реализацию async у меня.


I>Покажи простецкий кейс — выполнить долгий аджакс запрос + несколько подготовительных в начале + несколько заключительных в конце. Все выполняется последовательно.

var group = Async.group();
var preop1 = group.add(startPreop1());
var preop2 = group.applyAsync(startPreop2, [preop1]);
var preop3 = group.applyAsync(startPreop3, [preop2]);
var ajaxQuery = group.applyAsync(doLongLastingOp, [preop3]);
var postOp1 = group.applyAsync(startPostOp1, [ajaxQuery]);
var postOp2 = group.applyAsync(startPostOp2, [postOp1]);
var postOp3 = group.applyAsync(startPostOp3, [postOp2]);

R.apply(function(state) {
  switch (state.state) {
    case 'progress' :
      console.log('Progress is ' + state.progress);
  }
}, [group.progress]);

// cancellation:
group.cancel();

// functions:
function startPreop2() {
  // Staightfroward. between ajax and async API.
  return Net.call(....);
}


В качестве бонуса могу предложить примерную реализацию group:

  Скрытый текст
Async.group = function() {
  // Observable array.
  var ops = R.Arrays.variable();
  // Observable array of observables!
  var progresses = R.Arrays.mapValues(ops, function(op) {
    // Это будет behavior!
    return op.progress;
  });
  // Observable array of "progress values"
  var flatProgresses = R.Arrays.flatten(progresses);
  // Create behaviour which is sum of all progresses in the 
  // observable array (reduce operation).
  var totalProgress = R.Arrays.reduce(flatProgresses, 0, function(sum, state) {
    switch(state.state) {
      case 'progress': return sum + state.progress;
      case 'success' : return sum + 1;
      case 'error' : return sum;
      default: return sum; // log error
    }
  });
  var totalElems = R.Arrays.lenghtBehaviour(flatProgresses);
  // Среднее арифметическое, допустим, мы так считаем прогресс.
  var progress = R.apply(function(prog, count) {
    return count > 0 ? prog / count : 0;
  }, [totalProgress, totalElems]);
  
  return {
    'add' : function(op) {
      ops.push(op);
    },
    'applyAsync' : function() {
      var op = Async.applyAsync(arguments[0], arguments.slice(1));
      ops.push(op);
      return op;
    },
    'progress' : progress,
    'cancel' : function() {
      ops.foreach(function(op) { op.cancel(); });
    }
  }
}


I>Вся эта цепочка должна поддерживать отмену, прогресс и очень хочется легкой, прозрачной отладки.


С отладкой все просто. Можно на нужные функции ставить брекпоинты и внутри них все отлаживать. Обработку ошибок вся цепочка берет на себя (если что-то упадет, все остальное тоже упадет).

А вот с отменой и прогрессом все не так просто как кажется. Здесь проблема именно в постановке задачи. Один из примеров (с group) я привел. Но дело в том, что это не всегда удобно. Например, у меня была задача в следующем виде. Грузим А. По данным из А параллельно грузим Б и В. Получив и Б, и В по данным из них грузим Д. А еще получив данные из Б мы грузим Г (Г от В не зависит). Что такое "прогресс" у Д? Это чисто ее прогресс или прогресс с учетом прогресса из В, Б и А? И если второе, то сколько там А считать? Та же история с отменой. Если я отменяю Г, должен ли я отменить еще и Б с А? Или не должен (так как от них зависит Д)? Ответы могут очень сильно повлиять на API и на реализацию.

По времени. group я набросал за 15 минут. Для production (с отладкой) я подобный API соберу и отлажу за час примерно. Причем group будет написан один раз и будет использоваться в нескольких десятках мест в приложении (см. пример выше). При этом на выяснение _удобного_ для задачи API я потрачу как минимум еще один час. А то и больше. Но зато API получится удобным для практических задач. А не так, как необходимость распаковывать параметры из массива в Promise.awaitAll(...).then(...) (может, там все-таки есть что-то более удобное ). И потом все время разработки я буду наслаждаться удобным API а не делать какие-то магические приседания вокруг библиотеки с целью заставить ее работать.

>> Но пишется меньше, чем за неделю.

I>Это сказки. Чтото реактивное можно и за неделю. А чтото концептуальное, годное для продакшна в JS, пишется долго и мучительно. За примерами см например bacon.js и подобные либы.

В каком смысле концептуальное? Что-то в духе неомодернистского абстракционизма? Так оно же неудобное! Там обычно что-то не реализовано для моих конкретных задач, сделано не так или делается через кучу лишних действий. В результате "концептуальное" решение получается одинаково неудобным для 90% всех реальных практических задач.

А вот по поводу "годное для продакшна" — это не сказки. Это реальность. Я делал это своими руками и выпускал на базе реактивного программирования реальные проекты (и большие, и учебные). Как ни странно, в реальных приложениях не так много различных UI-компонентов. Основная база (кнопочки, элементы ввода, текст) пишется не более, чем за неделю. Затем по мере продвижения проекта добавляются другие элементы (вроде tabbed pane), суммарно еще может на неделю по времени. На выходе — набор ровно тех UI-контролов, которые нужны данному конкретному проекту. И 90% задач становятся рутинными и простыми. У меня не возникает кучи мест, куда нужно что-то стороннее допиливать или с библиотеками бороться.

По рискам/срокам/инвестициям (под новый проект, "старое — не брать") оно пойдет примерно так:

Итого. К концу первого двухнедельного спринат я получаю все ядро (мини-фреймворк), ядро UI, удобное для проекта API (не приходится натягивать ужа на ежа через день) и первый функциональный экран. Если не торопиться, заниматься сторонними требованиями, обсуждениями и т.п. в одиночку к концу месяца все на месте и все работает. Если не с нуля, то много берется с предыдущих проектов, пишется только networking layer и вперед — делать функциональность. Никаких изучений кучи трактатов о том, как заставить какую-то библиотеку работать в моих конкретных задачах. Все очень сильно упрощается, так как задачи для компонентов очень хорошо известны. Новые программисты вводятся по схеме: "2 часа на объяснение реактивного программирование, 2 часа — показать, где лежат наши UI-компоненты, еще час-два — на асинк. На второй день — в бой, писать функциональность".
Re[6]: В поисках Грааля каждые 16 минут на Земле ...
От: maxkar  
Дата: 18.03.15 23:03
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>делает то же самое что и у тебя как я понимаю. Только кроме того еще binding с UI:

CS>Без binding c UI я так понимаю это всё смысла не имеет.

Не знаю Sciter, но да, вроде бы это самое и делает.
UI нужен в обязательном порядке. Ради него все и затевается. У меня по историческим причинам (внедрялась первая версия на AS3 под Flash) все построение UI делается кодом. Для твоего примера (плюс мои определеня ранее) оно будет примерно так:

// elt будет element
var elt = UI.form(
  UI.label('X'), UI.input(x, x.set),
  UI.label('Y'), UI.input(y, y.set),
  UI.text('Z1: '), UI.text(z1), UI.text(" and Z2 "), UI.text(z2)
);

// Можно использовать и короткую версию для ввода:
UI.input(x) === UI.input(x, x.set)


Многие вещи вроде text — всеядные. Т.е. можно кормить как значение T, так и Behavior[T]. Разнесение read/set в input (и прочих двухсторонних биндингах) в некоторых экзотических случаях удобнее. Например, completion на нем чуть более естественно выглядит. Там вместо x.set пойдет вызов функции, которая запускает загрузку completion и вызывает x.set. Некоторые фильтрации можно так же делать. Через слушателей на x мне это не очень нравится. Какой смысл слушать изменения модели и "исправлять" ее, если можно предотвратить такое изменение?

К html ни разу не пробовал биндинги писать. Если хочется html-шаблоны, нужно будет что-то придумывать.

А вообще я не уверен, что для веба (html) нужно что-то тяжелое. Лично мне кажется, что для веба удобнее что-то обычное многостраничное но с поддержкой работы в большом количестве вкладок (а также закладок). Там по необходимости через тот жe jquery дополнительную функциональность подключать. В этом плане Sciter как раз интересный пример. Если я правильно понимаю, он в основном "однооконный" по модели взаимодействия с пользователем (не предполагается, что пользователь все ссылки/переходы будет делать в новую вкладку), так что некоторые вещи можно делать не так, как в чистом web.
Re[5]: В поисках Грааля каждые 16 минут на Земле ...
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 19.03.15 12:48
Оценка:
Здравствуйте, maxkar, Вы писали:

M>>>Всякие мелочи удобно реализуются. Тот же человекообразный Async на Behavior (вместо страшных promise в CPS будет Async.apply(fn, args...)). Желающие могут посмотреть реализацию async у меня.


I>>Покажи простецкий кейс — выполнить долгий аджакс запрос + несколько подготовительных в начале + несколько заключительных в конце. Все выполняется последовательно.

M>
M>var group = Async.group();
M>


Ужос. У тебя весь флоу раскидан непойми по чем. В любом месте самое главное находится где то еще.

I>>Вся эта цепочка должна поддерживать отмену, прогресс и очень хочется легкой, прозрачной отладки.


M>С отладкой все просто. Можно на нужные функции ставить брекпоинты и внутри них все отлаживать. Обработку ошибок вся цепочка берет на себя (если что-то упадет, все остальное тоже упадет).


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

M>А вот с отменой и прогрессом все не так просто как кажется. Здесь проблема именно в постановке задачи. Один из примеров (с group) я привел. Но дело в том, что это не всегда удобно. Например, у меня была задача


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


>>> Но пишется меньше, чем за неделю.

I>>Это сказки. Чтото реактивное можно и за неделю. А чтото концептуальное, годное для продакшна в JS, пишется долго и мучительно. За примерами см например bacon.js и подобные либы.

M>В каком смысле концептуальное? Что-то в духе неомодернистского абстракционизма? Так оно же неудобное!


Ну вот у тебя и вышло такой вот неомодернизм, красивый в теории, но неудобный на практике.

M>А вот по поводу "годное для продакшна" — это не сказки. Это реальность. Я делал это своими руками и выпускал на базе реактивного программирования реальные проекты (и большие, и учебные). Как ни странно, в реальных приложениях не так много различных UI-компонентов.


У меня в реальном проекте целая куча UI, рукопашного кода около 10мб, и там всё асинхронное-асинхронное-асинхронное.

M>Итого. К концу первого двухнедельного спринат я получаю все ядро


Если ты в отрыве от команды, то запросто. А если есть команда, то её нужно научить всем твоим приседаниям.
И вот реально проще взять обычные промисы, которые уже нативные чуть не всюду, и спокойно писать асинхронный код.
А в следующей версии JS можно будет убрать цепочки then в yield:

var cancel = new Cancel();
var progress = createProgress([5, 60, 25, 10], parentProgress);
var status = yield ajax('HEAD', url, cancel, progress[0]);

if(status.isValid())
   return Promise.reject(status.toError());

var archive = yield ajax('GET', url, cancel, progress[1]);
var content = yield unpack(archive, progress[2]);
var checksum = yield sha1.get(content, progress[3]);

return Promise.resolve({content:content, checksum:checksum});


Здесь кроме промисов совсем тонкий апи для отмены, прогреса и аджакса, самый мизер, прозрачнее некуда, бряки ставь куда хочешь прямо здесь.
Re[6]: В поисках Грааля каждые 16 минут на Земле ...
От: maxkar  
Дата: 23.03.15 14:57
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Ужос. У тебя весь флоу раскидан непойми по чем. В любом месте самое главное находится где то еще.

Это где это? Все идет так же последовательно. Ну ровно так же как в твоем примере с yield (и переписывается он туда механически). А group обычным программистам знать не нужно, это детали реализаций. Точно так же, как и детали реализации promise.


M>>С отладкой все просто. Можно на нужные функции ставить брекпоинты и внутри них все отлаживать. Обработку ошибок вся цепочка берет на себя (если что-то упадет, все остальное тоже упадет).

I>В случае сложного сценария это приведет к тому, что бряки нужно ставить по самым разным файлам вместо одного. Я думаю ты вспотеешь пыль глотать.
Да нет, не особо. Как-то так получается, что почти все нужные функции находятся в одном файле. А то, что находится в другом, обычно уже работает.

M>>А вот с отменой и прогрессом все не так просто как кажется. Здесь проблема именно в постановке задачи. Один из примеров (с group) я привел. Но дело в том, что это не всегда удобно. Например, у меня была задача

I>А это уже не интересно. На конкретной задаче вышло все не так удобно, как ты говорил. Отмена и прогресс это очень трудоемкие в отладке задачи, здесь хочется иметь максимум контроля.
А как же твой пример с Cancel? Где там максимум контроля? Где parent.cancel, например? Еще у меня есть претензии к parentProgress. Вот нет у меня такого и быть не может. Потому, что, например, метод getDepartmentState(deptId) может быть вызван из нескольких мест. И возвращает этот метод один и тот же промис для всех (ибо незачем одно и то же грузить 10 раз). Что там будет с progress? И не надо мне предлагать "все-таки запустить операцию два раза". Не хочу я под ограничения вашей технологии плясать.

Ты просто берешь некоторый "уже готовый" API и говоришь, что "вот как все просто с ним на задачах, подходящих для него". Это да, хорошо, если оно подходит под задачу. А если нет? Я не буду свои хотелки под API подгонять, я буду API подгонять под хотелки. Так что важен именно вопрос "хотелок". Реализация под них — дело чисто механическое.


M>>В каком смысле концептуальное? Что-то в духе неомодернистского абстракционизма? Так оно же неудобное!

I>Ну вот у тебя и вышло такой вот неомодернизм, красивый в теории, но неудобный на практике.
Пользовался. Удобный. Мне удобнее аппликативный вид чем промисы. Команда потихоньку к этому же склоняется


M>>А вот по поводу "годное для продакшна" — это не сказки. Это реальность. Я делал это своими руками и выпускал на базе реактивного программирования реальные проекты (и большие, и учебные). Как ни странно, в реальных приложениях не так много различных UI-компонентов.

I>У меня в реальном проекте целая куча UI, рукопашного кода около 10мб, и там всё асинхронное-асинхронное-асинхронное.

Ну и? Проблема в чем? Мы оцениваем вроде бы объем библиотечного кода. Это точно "библиотечные" 10мб? Какая разница, будут они использовать promise или мой async? И вдруг на моем async там всего 8мб получится?

M>>Итого. К концу первого двухнедельного спринат я получаю все ядро

I>Если ты в отрыве от команды, то запросто. А если есть команда, то её нужно научить всем твоим приседаниям.

Ну как сказать. Команду я научу за 2 часа реактивному программированию (это даже много, я презенташки проводил уже). На реактивщину у нас уже грамотный народ хочет слезть с ангуляра. На applicative api вокруг промисов есть задачи в трекере. Изучение UI api и набора базовых глаголов (типа той же сети) от используемого API не зависит. Нужно отдельный проект придумать, на котором его показать (ибо бюрократия и все такое, на реальном проекте приходится жрать кактус).

I>И вот реально проще взять обычные промисы, которые уже нативные чуть не всюду, и спокойно писать асинхронный код.

I>А в следующей версии JS можно будет убрать цепочки then в yield:
Ну посмотрел.
var group = Async.group();
var status = call(url, 'HEAD', group, 5);
var statusValue = group.apply(function(status) {
  if (status.isValid())
     throw status.toError();
  return status;
})

var archive = group.applyAsync(call, [url, 'HEAD', group', 60, statusValue]);
var content = group.apply(unpack, [archive], 25);
var checksum = group.apply(sha1.get, [content], 10);

return groupeapply(function(content, checksum) {
  return {content:content, checksum:checksum};
}, [content, checksum], 0);


Ну и какая принципиальная разница с yield? Ну чуть-чуть по-другому пишется. Async.apply используется вместо yield. Но какой-то концептуальной разницы я не вижу. Причем Aysnc работает уже сейчас и не нужно ждать, пока достаточно пользователей переедет на браузеры, которые поддерживают promise.

А что будет, если нужно выполнить несколько асинхронных действий параллельно. Причем с недетерминированным порядком ожидания. Т.е. "кто раньше готов исполняться, тот и выполняется".
var op1 = startOp1();
var op2 = startOp2();
var op3 = startOp3();

var opA = Async.applyAsync(doAsyncGood, [op1, op2]);
var opB = Async.applyAsync(doAsyncEvil, [op3, op2]);

return Async.apply(combineGoodAndEvil, [opA, opB]);


В зависимости от порядка завершения op1/op3 будет изменяться и порядок запуска doAsyncGood/doAsyncEvil. Это читаемо на yield/then будет? Обрати внимание, что случай одного/многих аргументов ничем не отличается. И для функций, возвращающих promise и возвращающих значение разница тоже не большая. И, кстати, что там с progress будет в этом сценарии?

I>Здесь кроме промисов совсем тонкий апи для отмены, прогреса и аджакса, самый мизер, прозрачнее некуда, бряки ставь куда хочешь прямо здесь.

А вот у меня про твой пример вопрос. sha1.get — это синхронная или асинхронная функция? И если синхронная, то как ее последний аргумент обрабатывается? Обрабатывается ли он yield или это все внутри функции происходит и она обязана за "weight" следить? И каждая асинхронная функция тоже обязана за переданным weight следить? Как-то накладно получается.

С бряками да, нужно ставить на функции. Но я ведь могу поменять Async.apply(smth, [...]) на Async.apply(function() { return smth.apply(null, arguments); }, [...]) и ставить бряки в ней. Но на моей практике это если и было нужно, то очень редко. А параллельное (менее детерминированное) выполнение в последовательном виде все равно не пишется (а если пишется, там все те же проблемы с брекпоинтами и недетерминированностью).

В общем, у меня Async API тоже тонкая прослойка над reactive. Включая отмену, прогресс и все остальные вызов (ajax обычно еще одним уровнем обкладывается ради выполнения требований). И я могу много разных API сделать. Только ты давай сначала показывай желаемый API или желаемые задачи. Потому что без этого дискуссия будет неконструктивная. Я могу собрать разные API для асинхронности, которые хорошо подходят под конкретный проект. И для любого API я могу подобрать требования, под которые этот API очень плохо подходит. Поэтому дискуссия "Покажи что-нибудь. А нет, это все сложно, вот мой API и у меня все просто" без предварительного описания требований особого смысла не имеет. Я в таком случае могу только показать, как на своей "элементной базе" я могу собирать разные API под разные задачи. И потом использовать эти API а не допиливать все по-месту.

P.S. Я не говорю, что Promises совсем отстой. Они удобны в некоторых классах задач. Только вопрос в том, что же это за классы. И второй вопрос: что делать, когда реальная задача не попадает ни в один из них?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.