LINUX.ORG.RU

Зачем нужны promises?

 


0

3

По моему, введение новой конструкции для асинхронных вычислений хорошо тогда, когда оно позволяет одно или несколько из следующих:

  • а) избавиться от тредов там, где приходится их использовать
  • б) избавиться от цепочки сообщений и заменить её обычным стеком исполнения
  • в) избавиться от разрыва лексической области видимости - пользоваться теми же локальными переменными, которые есть в начале асинхронного куска.

Пункт в) успешно решается замыканиями, т.е. futures тут не при делах. А как по остальным пунктам? Что-то не могу слёту понять, можно ли тут получить какую-то пользу. Или я ещё что-то упустил?

★★★★★

Ответ на: комментарий от newquestion

Specifically, when usage is distinguished, a future is a read-only placeholder view of a variable, while a promise is a writable, single assignment container which sets the value of the future.[4] Notably, a future may be defined without specifying which specific promise will set its value, and different possible promises may set the value of a given future, though this can be done only once for a given future. In other cases a future and a promise are created together and associated with each other: the future is the value, the promise is the function that sets the value – essentially the return value (future) of an asynchronous function (promise). Setting the value of a future is also called resolving, fulfilling, or binding it.

asaw ★★★★★
()
Ответ на: комментарий от asaw

Да, нашел, они там называют промисом функцию, возвращающую промис. Действительно бред написан. Хотя ты все равно неправильно перевел.

newquestion
()
Ответ на: комментарий от newquestion

Где бред написан?? Чего я неправильно перевел? Я вот это перевел: «a future is a read-only placeholder view of a variable, while a promise is a writable, single assignment container which sets the value of the future.» Прикрути ЧСВ уже.

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 1)
Ответ на: комментарий от newquestion

там называют промисом функцию, возвращающую промис.

4.2 же! the promise is the function that sets the value. Неужели так сложно читать с открытыми глазами?

asaw ★★★★★
()
Ответ на: комментарий от asaw

Бред написан на википедии. А ты еще исказил смысл этого бреда.

a future is a read-only placeholder view of a variable, while a promise is a writable, single assignment container which sets the value of the future

нет, тут про другое написано. Ты думаешь, что перевел (на самом деле — скопипастил с рупедии и выдал за свой перевод) вот этот кусок

a future and a promise are created together and associated with each other: the future is the value, the promise is the function that sets the value – essentially the return value (future) of an asynchronous function (promise).

Тут написано: фьючер и промис создаются совместно и ассоциированы друг с другом: фьючер это значение, промис это функция, которая возвращает знчение(фьючер), асинхронной функции(промиса).

Теперь сравни это со своей *версией*

Хотя, как я уже сказал, написана чушь. А твой якобы перевод, это вообще эффект сломаного телефона, ты даже не понял толком, какой кусок ты «перевел»

newquestion
()
Ответ на: комментарий от newquestion

возвращает

точней, не возвращает, а устонавливает возвращаемое, там написано

//fixed

newquestion
()
Ответ на: комментарий от newquestion

нет, тут про другое написано. Ты думаешь, что перевел (на самом деле — скопипастил с рупедии и выдал за свой перевод) вот этот кусок

Я это вообще не переводил и не копипастил, а сам сформулировал. А потом нашел в вике то же самое.

функция, которая возвращает знчение(фьючер)

Где ты там увидел слово «возвращает»??? Ты если строишь из себя буквоеда, то строй до конца. (ага, опомнился мальчик)

Хотя, как я уже сказал, написана чушь. А твой якобы перевод, это вообще эффект сломаного телефона, ты даже не понял толком, какой кусок ты «перевел»

Чушь - это то, что ты думаешь про написанное. Какой кусок я «перевел» я тебе процитировал. Возьми и перечитай.

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 1)
Ответ на: комментарий от asaw

а сам сформулировал

да, ты переформулировал, исказив смысл. Я так и написал, твое определение, в итоге вообще никакому не соответствует, так как ты не понял смысл прочитанного

Где ты там увидел слово «возвращает»??? Ты если строишь из себя буквоеда, то строй до конца.

return

Я не буквоед если бы твой перевод был литературным, но отражающим смысл, никакого разговора бы не было.

newquestion
()
Ответ на: комментарий от newquestion

Если ты не буквоед, то объясни какой смысл я якобы исказил, а не сыпь цитатами. С чего ты вообще взял, что ты знаешь лучше чем я и авторы википедии? У меня то с ними разночтений нет, заметь.

asaw ★★★★★
()
Ответ на: комментарий от asaw

Последний раз

вот твоя вольная интерпретация

Если по-простому, то promise это то, куда помещается результат тем, кто его вычисляет, а future - это то, откуда он забирается тем, кому он нужен.

вот оригинал

a future and a promise are created together and associated with each other: the future is the value, the promise is the function that sets the value – essentially the return value (future) of an asynchronous function (promise).

Вот адекватный перевод, причем почти дословный

фьючер и промис создаются совместно и ассоциированы друг с другом: фьючер это значение, промис это функция, которая устанавливает значение — по сути, возвращаемое знчение(фьючер), асинхронной функции(промиса).

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

newquestion
()
Ответ на: комментарий от newquestion

вот оригинал

4.2. Оригинал я цитировал. Учись читать.

Малыш, а вот это вот ты забыл:

In other cases

?

Почему для тебя существуют только «other cases»?

И второе - самое главное, поскольку с википедией ты тоже не согласен: где твои обоснования того, что написанное там неправильно?

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 2)
Ответ на: комментарий от asaw

Почитай серьезные книжки, употреби промисы в коде, хоть в том же JS, поймешь (может быть). Что это такое ты можешь посмотреть в первом же ответе на твое сообщение.

newquestion
()
Ответ на: комментарий от newquestion

Ну то есть ты что-то где-то слышал, но обосновать ничего сам не можешь. И почему ты вообще думаешь, что свет клином сошелся на JS?

asaw ★★★★★
()
Ответ на: комментарий от anonymous

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

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

.done .fail

Ну это же обманка. Развернули вложенность в линию, проще-то по сути от этого не стало, скорее наоборот.

Плюс ловятся исключения. Для замыканий нужен try/catch в каждой функции, здесь хватит одного блока в единственном месте.

Ну, вот это, пожалуй, наклёвывается первая ощутимая польза. Хотя это может зависеть от языка. Не знаю, как в лиспе с этим.

А по поводу «запустить несколько одновременно» сами джаваскриптеры не пришли к единому мнению и разругались. Но я бы сделал несколько тредов, таблицу результатов и каждый тред по успеху заполнял бы свой результат и смотрел, не последний ли он. Хотя, наверное, это и есть реализация обещаний?

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Чтобы бить на функции, надо задумываться, как красивее оформить. Время отнимает. Вот если бы были best practices, тогда можно было их ляпать, не приходя в сознание. В принципе, промисы именно для того и нужны, для общего интерфейса.

..

Они, по ощущению, пошли в массы именно от JavaScript-а. JS принципиально устроен, что там один-единственный тред. Если считать факториал миллиона — все зависнет сразу и насмерть.

Например, если в JS-консоли (F12 -> Console) ввести такой код, то все повиснет. У хрома — текущая вкладка, никакую ссылку не нажмешь, у фокса — совсем все, придется перезапускать браузер (если никакой защиты не сделали, тут не в курсе).

for (var i = 0; i < 1000; i++) {
    // до 1000 никогда не дойдет
    if (i > 100) i=0;
}

.

Взамен — никаких проблем с гонками и синхронизацией.

Кроме того, I/O используется неблокирующий, так что можем загружать 1000 файлов одновременно, и ничего висеть не будет. Мы делаем отметку, мол, хочу скачать файл с dropbox, и какую функцию позвать, когда скачается. С этого момента для JS-а никаких блокировок нет, он идет шуровать программу дальше.

...

Насчет одновременности в JS — товарищи просто адово буквоедствуют.

asyncAjax1();
asyncAjax2();
asyncAjax3();

Запросы пойдут параллельно. Если пинг до сервера — минута, то через 60 секунд все результаты будут готовы.

А «не одновременно» пойдет отмашка за запуск, т.е. если инициировать каждый запрос — десяток инструкций, то комп освободится через 3 * 10 инструкций. Эти копейки никого не интересуют, но вон как из-за них развернулись.

...

Да, запрячь, чтобы каждый тред проверял, не последний ли он, совершенно правильно, внутри все так и будет. Или библиотекой на неблокирующий I/O, тогда проверяться будет каждый коллбэк.

Вопрос только в интерфейсе и best practices, как это оформлять.

Напрямую это будет реализация защелки. В Java, не Javascript, это CountDownLatch, встроена в стандартную библиотеку.

Как это для обещаний выглядит — следующий пост.

anonymous
()
Ответ на: комментарий от anonymous

Так вот, пример.

...

Пишем простую игру, скажем, крестики-нолики, графически оформленную. И нужно загрузить три картинки: крестик, нолик, поле, [cross, zero, board].

try {
    globalImgBoard = loadFile('board');
    globalImgCross = loadFile('cross');
    globalImgZero = loadFile('zero');
  
    startTheGame();
    
} catch (err) {
    // атас, кто-то не загрузился, выходим
    exit(err);
}

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

И автоматом получаем гарантию: игра запустится только после того, как загрузили все три картинки, и никак не раньше.

...

Теперь, если запросы асинхронные, то так не получится. loadFile возвращается мгновенно, до того, как файл реально загрузился. Так что startTheGame получает дырку от бублика, а не ресурсы.

Поэтому коллбэки выстраиваются в лесенку:


loadFileAsync('board').onSuccess(function(resp) {
    // Кладем в глобальную переменную содержимое-результат, и идем
    // к следующему файлу
    globalImgBoard = resp;
    loadFileAsync('cross').onSuccess(function(resp) {
    
        globalImgCross = resp;
        loadFileAsync('zero').onSuccess(function(resp) {
        
            globalImgZero = resp;
            startTheGame();
        }).onFail(/*обработка 3*/)
    }).onFail(/*обработка 2*/
}).onFail(/*обработка 1*/);

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

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

Что еще плохо - если до сервера пинг 20 секунд, то картинки идут последовательно, и на все три уйдет минута. Долго.

anonymous
()
Ответ на: комментарий от anonymous

Но можно навелосипедить защелку-latch. Каждая загруженная картинка уменьшит счетчик на 1; как только дойдем до нуля, то все загружено, начинаем игру. (замнем для ясности сами результаты, которые globalImgCross)

global notLoadedImgCount = 3;


function notifyLatch() {
    notLoadedImgCount -= 1;
    if (notLoadedImgCount == 0) {
        // Начинаем игру
        startTheGame();
    }
}

function notifyLatchFail() {
    // Или совсем не трогать, тогда до нуля никогда и не дойдет
    notLoadedImgCount = -1;
    exit('some-error');
}

loadFileAsync('board').onSuccess(notifyLatch).onFail(notifyLatchFail)
loadFileAsync('cross').onSuccess(notifyLatch).onFail(notifyLatchFail)
loadFileAsync('zero').onSuccess(notifyLatch).onFail(notifyLatchFail)

В общем-то, выглядит симпатишно. Можно счетчик вычислять автоматически, по числу файлов; объединить его, notifyLatch и notifyLatchFail в отдельный объект-неймспейс; подкрутить общение функций на fail и на done, чтобы прекращать кого-то ждать сразу, после первой ошибки. И получится что-то близкое к промису.

when(
    loadFileAsync('board'), // этой штуковине внутрях мы сможем прицепить onSuccess и onFail
    loadFileAsync('cross'),
    loadFileAsync('zero')
)
.done(startGame)
.fail(showError);

Суть, конечно, все та же самая, но кода ощутимо меньше, под коврик заметено много пыли, что крайне радует.

...

Опять же, loadFileAsync получается абстрактным вычислением.

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

Например, скачали поле, оно оказалось темного цвета, значит, другие значки нужно скачивать светлые, для контраста. Если поле белое, то значки качаем темные.

Тут надо только API оформить, чтобы вызовы друг к другу подходили; у jQuery это, в общем-то, получилось.

Пример ( http://jsfiddle.net/dK9tc/ ) с той же страницы, что и предыдущий, с параллельными запросами. ( http://stackoverflow.com/questions/16026942/how-do-i-chain-three-asynchronous... )

Собственно, от запроса к запросу можно футболить объект-контейнер, в котором будут лежать результаты всех уже сделанных запросов.

...

В итоге — да, промисы ничего принципиально нового не дают. Это получается единобразный (и лаконичный!) интерфейс для того, чтобы провести пачку асинхронных запросов. Хоть последовательно (если запросы друг от друга зависят), хоть параллельно (если не зависят, заодно и быстрее).

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

anonymous
()
Ответ на: комментарий от anonymous

Во как надо отвечать на вопросы! А я уж думал, совсем измелчал народ. Но есть, есть ещё титаны.

den73 ★★★★★
() автор топика
Ответ на: комментарий от anonymous

не пытаясь оценивать слова Vit'a и твои по сути вопроса (о том когда чего появилось), хотел бы тебе указать что твой вопрос о том «как можно если не знаешь истории» вообще говоря очень наивен: узкоспециальные знания очень редко зависят от «общего» владения темой. В том смысле что можно быть прекрасным программистом и не знать когда там чего изобрели от слова вообще. Тут нет противоречия. Это явление даже название имеет - профессиональный кретинизм (опять таки обращаю внимание что я не даю оценки конкретным личностям в треде хотя бы потому что сам не в теме). Более того, если социализм ставит целью преодоление такого кретинизма и воспитание гармоничной личности, то капитализм, пытаясь оптимизировать расходы, естественным путём приходит к воспитанию «профессиональных кретинов». Отсюда, по-моему, кстати растут ноги и у тучи учёных, которые верят в бога.

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 1)
Ответ на: комментарий от AndreyKl

Годный вброс на тему учёных, которые верят в Бога. Поддержу. На самом деле, к думающему человеку вера приходит иначе, чем к тому, в ком её воспитывают с детства «конфетками от ангелов».

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

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

3. Ну и нужно понимать, что тема «веры в бога» слишком широка, у неё много независимых аспектов. Например, Яхве - это некий праздношатающийся засланец из космоса, который случайно залетел и научил избранный народ, как бить по яйцам. Бог-творец, устроивший жизнь и Космос так, что они продолжают существовать миллиарды лет без всяких лаганий и core dump-ов - это другой аспект темы. Личный ангел-хранитель, который спасает в критической ситуации - это третий аспект (даже из ярых атеистов очень многие под напором жизненного опыта бывают вынуждены что-то такое признать). Иисус Христос сочетает не менее 5 аспектов: аграрно-астрономическую символику, этическое учение, магический клан, пророчество и государственную систему.

Так что я не соглашусь о связи между капитализмом и вероя в Бога. Не нужно думать о вере с позиции недалёкого лектора по атеизму и для тебя откроются новые горизонты. Аминь.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Но не хочу работать на залистывание прекрасной статьи Анонимуса! Премногая благодарность! В таком контексте промисы безусловно выглядят полезными.

Теперь надо подумать, чем промисы могут пригодиться в других ситуациях и в других языках. И как их ещё можно было оформить. Например, у меня на ttk::notebook есть проблема - не могу хорошо навесить событие на переключение закладки. Внутрь события нужно передать какую-то информацию, но сделать это никак невозможно. Можно лишь надеяться, что такое событие будет происходить одно за единицу времени, но и гарантировать это никак нельзя, не влезая в исходник.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

вообще, если правда интересуешься, погляди курс «реактив программинг он скала», там промисы подробно разжёвываются (правда не совсем так как в яваскрипте, но практически неотличимо). лично для меня это в первую очередь моноид для ассинхронных вычислений. ну и этим всё сказано, правда чтобы понять, ты должен быть в курсе что такое моноид.

AndreyKl ★★★★★
()
Последнее исправление: AndreyKl (всего исправлений: 1)
Ответ на: комментарий от den73

мне жаль тебя расстраивать, но «что то такое признают» - это мягко говоря не агрумент в научном сообществе. а вера в бога - она таки от незнания. если ты знаком с диалектическим материализмом, то очень трудно не принять его _аргументированно_ как с мировоззврение, а вере в бога там просто не остаётся места.

AndreyKl ★★★★★
()
Ответ на: комментарий от den73

Очень лестные отзывы, спасибо, прямо приятно писать.

...

Насчет других языков и ситуаций — Rx.java. http://habrahabr.ru/post/265269/

Я про нее только читал, но идея там примерно такая.

В коллбэке кнопки ставится штуковина-эмиттер

PublishSubject publishSubject = PublishSubject.create();

...
void buttonCallback(Event event) {
    publishSubject.onNext(event);
}

...

И на эту штуковину (PublishSubject, наследует класс Observer) можно подписаться и начать строить пайп. Каждое событие по нему пройдет, и, стало быть, асинхронно вызовет все стадии.

publishSubject
    .map(e -> e.hashCode())
    .map(code -> code / 10) //просто потому, что можем!
    .subscribe(s -> System.out.println(s));

Превратили в хэшкод и распечатали, причем это сделано асинхронно. Кроме map-ов есть и другие функции, например, zip, flatmap и фиг знает, какие еще.

Очень интересная фишка - разные стадии обработки можно перекидывать между разными тредами.

Например, событие появилось в UI-треде, map-ается в тред автодополнения, где находится пяток вариантов, и subscribe-ится обратно в UI-поток, который может перерисовать все, что нужно.

// Работать не будет, писал из головы
publishSubject.
    .subscribeOn(Threads.background()) // Все, что до subscribe, пойдет в фоновом треде
    .observeOn(Threads.ui())           // а последняя стадия — в UI-ном

    .map(event -> findAutocomplete(e.text())
    .subscribe(suggestedOptions->
        // какие-нибудь вычисления
        autocompleteWindow.setOptions(suggestedOptions)
        );

А вот это — пример из статьи. myImageView.setImageBitmap(bitmap) — Android-ная штуковина, должна выполняться в UI-ном треде, а retrieveImage, наоборот, ходит по сети и выполняется в бэкграунде.

myObservableServices.retrieveImage(url)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

...

Как у Тикля с реактивным программированием — понятия не имею, ни строчки на нем не писал. Но, подозреваю, такой развесистой библиотеки там точно нет.

С табом непонятно. Почему бы не закинуть новое сообщение в буфер, пока старое не отработало до конца?

anonymous
()
Ответ на: комментарий от anonymous

Очень интересная фишка - разные стадии обработки можно перекидывать
между разными тредами.

В тикле треды - это новодел и не в традиции. Работа идёт через очередь сообщений и вообще всё событийно-ориентировано. Т.е. сама по себе ситуация, когда алгоритм разорван на цепочку обработчиков событий с потерей понятия о стеке, для тикля типична и мне это не нравится. Для реализации перебросов сообщений куда-либо можно класть скрипт со всеми сделанными подстановками в очередь, а в другом месте позже делать ему eval. Я так делаю. Так можно реализовать саму идею отложенного вычисления. Замыкания в тикле тоже, по сути, есть (на уровне текстовых подстановок). С анализом структуры сообщения несколько сложнее, но с плясками тоже делается. Ну и плюс ещё есть Quoting Hell, который, по хорошему, вынуждает делать такой код, предназначенный для последующего выполнения, сильно отличающимся на вид от обычного кода.

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

Но это не относится к нашей теме. Вопрос про промисы абстрактен, просто смотрю и думаю, смогу ли где-то их плодотворно применить. А начался он с того, что кто-то на comp.lang.tcl спросил, есть ли промисы в тикле и ему было отвечено, что есть корутины, да и вообще, что «промисы не нужны».

Например, событие появилось в UI-треде, map-ается в тред автодополнения, где находится пяток вариантов, и subscribe-ится обратно в UI-поток, который может перерисовать все, что нужно.

А вот здесь гонка между вводом пользователя и вводом, порождённым комплишеном. У меня пока это именно для комплишена не решено. Считаю правильным блокировать интерфейс на время отработки комплишена, а все события ввода пользователя, пришедшие в заблокированный интерфейс, откладывать на потом. Вроде в тикле это делается, хотя я пока не могу исправить баги в этом, так что уверенность не 100%, хотя известно, что баги мои.

den73 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.