stringtranslate.com

Будущее и обещания

В информатике future , promise , delay и deferred относятся к конструкциям, используемым для синхронизации выполнения программ в некоторых языках параллельного программирования . Они описывают объект, который действует как прокси для результата, который изначально неизвестен, обычно потому, что вычисление его значения еще не завершено.

Термин «обещание» был предложен в 1976 году Дэниелом П. Фридманом и Дэвидом Уайзом [1] , а Питер Хиббард назвал его «возможным» [2] . Несколько схожая концепция «будущее» была введена в 1977 году в статье Генри Бейкера и Карла Хьюитта [3] .

Термины future , promise , delay и deferred часто используются взаимозаменяемо, хотя некоторые различия в использовании между future и promise рассматриваются ниже. В частности, когда использование различается, future является представлением переменной только для чтения , в то время как promise является записываемым контейнером с одним назначением , который устанавливает значение future. В частности, future может быть определен без указания того, какое конкретное promise установит его значение, и различные возможные promise могут устанавливать значение данного future, хотя это можно сделать только один раз для данного future. В других случаях future и promise создаются вместе и связываются друг с другом: future является значением, promise является функцией, которая устанавливает значение – по сути, возвращаемым значением (future) асинхронной функции (promise). Установка значения future также называется разрешением , выполнением или связыванием его.

Приложения

Futures и promises возникли в функциональном программировании и связанных с ним парадигмах (таких как логическое программирование ) для отделения значения (future) от того, как оно вычисляется (promise), что позволяет выполнять вычисления более гибко, в частности, путем их распараллеливания. Позже они нашли применение в распределенных вычислениях , для сокращения задержек от круговых передач связи. Еще позже они стали более распространенными, позволяя писать асинхронные программы в прямом стиле , а не в стиле продолжения-передачи .

Неявный против явного

Использование futures может быть неявным (любое использование future автоматически получает его значение, как если бы это была обычная ссылка ) или явным (пользователь должен вызвать функцию, чтобы получить значение, например, getметод java.util.concurrent.Futureв Java ). Получение значения явного future можно назвать stinging или forcing . Явные futures могут быть реализованы как библиотека, тогда как неявные futures обычно реализуются как часть языка.

В оригинальной статье Бейкера и Хьюитта описывались неявные будущие, которые естественным образом поддерживаются в модели акторов вычислений и чистых объектно-ориентированных языках программирования, таких как Smalltalk . В статье Фридмана и Уайза описывались только явные будущие, что, вероятно, отражало сложность эффективной реализации неявных будущих на стандартном оборудовании. Сложность заключается в том, что стандартное оборудование не имеет дела с будущими для примитивных типов данных, таких как целые числа. Например, инструкция add не знает, как обращаться с . В чистых языках акторов или объектов эта проблема может быть решена путем отправки сообщения , которое просит будущее сложить с собой и вернуть результат. Обратите внимание, что подход с передачей сообщений работает независимо от того, когда завершается вычисление, и что не требуется никаких стигментаций/форсинга.3 + future factorial(100000)future factorial(100000)+[3]3factorial(100000)

Обещание конвейеризации

Использование фьючерсов может значительно сократить задержку в распределенных системах . Например, фьючерсы позволяют реализовать конвейеризацию обещаний [4] [5] , как это реализовано в языках E и Joule , который также назывался call-stream [6] в языке Argus .

Рассмотрим выражение, включающее обычные удаленные вызовы процедур , например:

t3 := ( xa() ).c( yb() )

который может быть расширен до

t1 := xa(); t2 := yb(); t3 := t1.c(t2);

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

Используя фьючерсы, приведенное выше выражение можно записать

t3 := (x <- a()) <- c(y <- b())

который может быть расширен до

t1 := x <- a(); t2 := y <- b(); t3 := t1 <- c(t2);

Здесь используется синтаксис языка E, где означает асинхронную x <- a()отправку сообщения в . Всем трем переменным немедленно назначаются будущие значения для их результатов, и выполнение переходит к последующим операторам. Более поздние попытки разрешить значение могут вызвать задержку; однако конвейеризация может сократить количество необходимых циклов. Если, как в предыдущем примере, , , , и находятся на одной и той же удаленной машине, конвейерная реализация может выполнить вычисления с одним циклом вместо трех. Поскольку все три сообщения предназначены для объектов, которые находятся на одной и той же удаленной машине, необходимо отправить только один запрос и получить только один ответ, содержащий результат. Отправка не будет блокироваться, даже если и находятся на разных машинах друг с другом или в или .a()xt3xyt1t2t3t1 <- c(t2)t1t2xy

Конвейеризацию обещаний следует отличать от параллельной асинхронной передачи сообщений. В системе, поддерживающей параллельную передачу сообщений, но не конвейерную, отправка сообщений x <- a()и y <- b()в приведенном выше примере может выполняться параллельно, но отправка t1 <- c(t2)должна будет ждать, пока оба t1и t2не будут получены, даже если x, y, t1, и t2находятся на одной и той же удаленной машине. Относительное преимущество конвейерной обработки в задержке становится еще больше в более сложных ситуациях, включающих много сообщений.

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

Просмотры только для чтения

В некоторых языках программирования, таких как Oz , E и AmbientTalk , можно получить представление future, доступное только для чтения , что позволяет читать его значение при разрешении, но не позволяет разрешать его:

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

Фьючерсы, специфичные для потока

Некоторые языки, такие как Alice ML , определяют будущие события, которые связаны с определенным потоком, который вычисляет значение будущего. [9] Это вычисление может начинаться либо активно, когда будущее создается, либо лениво , когда его значение впервые требуется. Ленивое будущее похоже на thunk в смысле отложенного вычисления.

Alice ML также поддерживает будущие события, которые могут быть разрешены любым потоком, и называет их promises . [8] Такое использование promise отличается от его использования в E, как описано выше. В Alice promise не является представлением только для чтения, и конвейеризация promise не поддерживается. Вместо этого конвейеризация естественным образом происходит для будущих событий, включая те, которые связаны с promises.

Блокирующая и неблокирующая семантика

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

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

В качестве примера первой возможности в C++11 поток, которому необходимо значение future, может блокироваться до тех пор, пока оно не станет доступно, вызывая функции-члены wait()или get(). Тайм-аут также может быть указан для ожидания с помощью функций- членов wait_for()или wait_until(), чтобы избежать неопределенной блокировки. Если future возник из вызова , std::asyncто блокирующее ожидание (без тайм-аута) может привести к синхронному вызову функции для вычисления результата в ожидающем потоке.

Связанные конструкции

Будущие события являются частным случаем примитива синхронизации " события ", который может быть завершен только один раз. В общем случае события могут быть сброшены в начальное пустое состояние и, таким образом, завершены столько раз, сколько необходимо. [11]

I -var (как в языке Id ) — это future с блокирующей семантикой, как определено выше. I-structure — это структура данных , содержащая I-var. Связанная конструкция синхронизации, которая может быть установлена ​​несколько раз с разными значениями, называется M-var . M-var поддерживают атомарные операции для взятия или помещения текущего значения, где взятие значения также возвращает M-var в его первоначальное пустое состояние. [12]

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

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

Отношения между выразительностью различных форм будущего

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

Для реализации неявных ленивых потокоспецифичных будущих событий (например, как в Alice ML) в терминах не потокоспецифичных будущих событий необходим механизм для определения того, когда значение будущего события необходимо в первую очередь (например, конструкция WaitNeededв Oz [13] ). Если все значения являются объектами, то достаточно реализовать прозрачные объекты пересылки, поскольку первое сообщение, отправленное пересылателю, указывает на необходимость значения будущего события.

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

Стратегия оценки

Стратегия оценки фьючерсов, которую можно назвать вызовом по фьючерсу , недетерминирована: значение фьючерса будет оценено в какой-то момент между моментом создания фьючерса и моментом использования его значения, но точное время заранее не определено и может меняться от запуска к запуску. Вычисление может начаться сразу после создания фьючерса ( eagy evaluation ) или только тогда, когда значение действительно необходимо ( lazy evaluation ), и может быть приостановлено на полпути или выполнено за один запуск. После того, как значение фьючерса назначено, оно не пересчитывается при будущих обращениях; это похоже на мемоизацию, используемую в call by need .

АЛенивый будущий — это будущий, который детерминированно имеет ленивую семантику вычисления: вычисление значения будущего начинается, когда значение впервые требуется, как в вызове по потребности. Ленивые будущие используются в языках, стратегия вычисления которых по умолчанию не является ленивой. Например, вC++11такие ленивые будущие могут быть созданы путем передачиstd::launch::deferredполитики запуска вstd::async, вместе с функцией для вычисления значения.

Семантика будущего в модели актора

В модели актора выражение формы future <Expression>определяется тем, как оно реагирует на Evalсообщение со средой E и клиентом C следующим образом: Будущее выражение отвечает на Evalсообщение, отправляя клиенту C вновь созданный актор F (прокси для ответа evaluating <Expression>) в качестве возвращаемого значения одновременно с отправкой <Expression>сообщения Evalсо средой E и клиентом C. Поведение F по умолчанию следующее:

Однако некоторые фьючерсы могут обрабатывать запросы особым образом, чтобы обеспечить большую параллельность. Например, выражение 1 + future factorial(n)может создать новый фьючерс, который будет вести себя как число 1+factorial(n). Этот трюк не всегда работает. Например, следующее условное выражение:

if m>future factorial(n) then print("bigger") else print("smaller")

приостанавливается до тех пор, пока будущее factorial(n)не ответит на запрос, mбольше ли оно само.

История

Конструкции future и/или promise были впервые реализованы в таких языках программирования, как MultiLisp и Act 1. Использование логических переменных для связи в языках программирования concurrent logic было довольно похоже на futures. Они начались в Prolog с Freeze и IC Prolog и стали настоящим примитивом concurrency с Relational Language, Concurrent Prolog , guarded Horn clauses (GHC), Parlog , Strand , Vulcan , Janus , Oz-Mozart , Flow Java и Alice ML . I-var с одним присваиванием из языков программирования dataflow , происходящая из Id и включенная в Reppy's Concurrent ML , очень похожа на переменную concurrent logic.

Метод конвейеризации обещаний (использование будущих событий для преодоления задержек) был изобретен Барбарой Лисков и Любой Шрирой в 1988 году [6] и независимо Марком С. Миллером , Дином Трибблом и Робом Джеллингхаусом в контексте проекта Xanadu около 1989 года [14].

Термин «обещание» был придуман Лисковым и Шрирой, хотя они называли механизм конвейеризации именем call-stream , которое сейчас используется редко.

Как дизайн, описанный в статье Лискова и Шриры, так и реализация конвейеризации обещаний в Xanadu имели ограничение, что значения обещаний не были первоклассными : аргумент или значение, возвращаемое вызовом или отправкой, не могли быть напрямую обещанием (поэтому приведенный ранее пример конвейеризации обещаний, который использует обещание для результата одной отправки в качестве аргумента другой, не был бы напрямую выражен в дизайне потока вызовов или в реализации Xanadu). Похоже, что обещания и потоки вызовов никогда не были реализованы ни в одной публичной версии Argus, [15] языка программирования, используемого в статье Лискова и Шриры. Разработка Argus прекратилась около 1988 года. [16] Реализация конвейеризации обещаний в Xanadu стала общедоступной только с выпуском исходного кода для Udanax Gold [17] в 1999 году и никогда не была объяснена ни в одном опубликованном документе. [18] Более поздние реализации в Joule и E полностью поддерживают обещания и решатели первого класса.

Несколько ранних языков акторов, включая серию Act, [19] [20] поддерживали как параллельную передачу сообщений, так и конвейерную обработку сообщений, но не конвейеризацию обещаний. (Хотя технически возможно реализовать последнюю из этих функций в первых двух, нет никаких доказательств того, что языки Act делали это.)

После 2000 года произошло значительное возрождение интереса к futures и promises из-за их использования в отзывчивости пользовательских интерфейсов и в веб-разработке из-за модели передачи сообщений « запрос-ответ»FutureTask . Несколько основных языков теперь имеют языковую поддержку futures и promises, наиболее заметно популяризированную в Java 5 (анонсирована в 2004 году) [21] и конструкциях async/await в .NET 4.5 (анонсирована в 2010 году, выпущена в 2012 году) [22] [23] во многом вдохновленную асинхронными рабочими процессами F#, [24] которые датируются 2007 годом. [25] Впоследствии это было принято другими языками, в частности Dart (2014), [26] Python (2015), [27] Hack (HHVM) и проектами ECMAScript 7 (JavaScript), Scala и C++ (2011).

Список реализаций

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

Список концепций, связанных с будущими событиями и обещаниями по языкам программирования

Языки, также поддерживающие конвейеризацию обещаний, включают:

Список реализаций фьючерсов на основе библиотек

Корутины

Будущие события могут быть реализованы в сопрограммах [27] или генераторах [103] , что приводит к той же стратегии оценки (например, кооперативная многозадачность или ленивая оценка).

Каналы

Futures можно легко реализовать в каналах : future — это одноэлементный канал, а promise — это процесс, который отправляет данные в канал, выполняя future. [104] [105] Это позволяет реализовывать futures в параллельных языках программирования с поддержкой каналов, таких как CSP и Go . Получающиеся futures являются явными, поскольку доступ к ним должен осуществляться путем чтения из канала, а не только путем оценки.

Смотрите также

Ссылки

  1. ^ Фридман, Дэниел; Дэвид Уайз (1976). Влияние аппликационного программирования на многопроцессорную обработку . Международная конференция по параллельной обработке. С. 263–272.
    Предварительная версия: Friedman, Daniel; Wise, David (апрель 1978 г.). «Аспекты прикладного программирования для параллельной обработки». IEEE Transactions on Computers . C-27 (4): 289–296. CiteSeerX 10.1.1.295.9692 . doi :10.1109/tc.1978.1675100. S2CID  16333366. 
  2. ^ Хиббард, Питер (1976). Средства параллельной обработки . Новые направления в алгоритмических языках, (ред.) Стивен А. Шуман, IRIA, 1976.
  3. ^ Генри Бейкер; Карл Хьюитт (август 1977 г.). Инкрементная сборка мусора процессов. Труды симпозиума по языкам программирования искусственного интеллекта. ACM SIGPLAN Notices 12, 8. стр. 55–59. Архивировано из оригинала 4 июля 2008 г. Получено 13 февраля 2015 г.
  4. ^ Promise Pipelining на erights.org
  5. ^ Promise Pipelining на вики C2
  6. ^ ab Барбара Лисков; Люба Шрира (1988). «Promises: Linguistic Support for Efficient Asynchronous Procedure Calls in Distributed Systems». Труды конференции SIGPLAN '88 по разработке и реализации языков программирования; Атланта, Джорджия, США . ACM. стр. 260–267. doi :10.1145/53990.54016. ISBN 0-89791-269-1.Также опубликовано в ACM SIGPLAN Notices 23 (7).
  7. Надежные обещания с отсрочкой Dojo, Site Pen, 3 мая 2010 г.
  8. ^ ab "Promise", Alice Manual , DE: Uni-SB, архивировано из оригинала 8 октября 2008 г. , извлечено 21 марта 2007 г.
  9. ^ ab "Future", руководство Alice , DE: Uni-SB, архивировано из оригинала 6 октября 2008 г. , извлечено 21 марта 2007 г.
  10. ^ Обещание, права E
  11. ^ 500 строк или меньше, «Веб-сканер с асинхронными сопрограммами» А. Джесси Джирю Дэвиса и Гвидо ван Россума говорит: «реализация использует asyncio.Event вместо Future, показанного здесь. Разница в том, что Event можно сбросить, тогда как Future не может перейти из разрешенного обратно в ожидающее».
  12. Control Concurrent MVar, Haskell, архивировано из оригинала 18 апреля 2009 г.
  13. WaitNeeded, Mozart Oz, архивировано из оригинала 17 мая 2013 г. , извлечено 21 марта 2007 г.
  14. Promise, Sunless Sea, архивировано из оригинала 23 октября 2007 г.
  15. ^ Аргус, Массачусетский технологический институт
  16. ^ Лисков, Барбара (26 января 2021 г.), Распределенные вычисления и Argus, Устная история, IEEE GHN
  17. Gold, Udanax, архивировано из оригинала 11 октября 2008 г.
  18. ^ Трубопровод, права E
  19. Генри Либерман (июнь 1981 г.). «Предварительный просмотр Акта 1». MIT AI Memo 625 .
  20. Генри Либерман (июнь 1981 г.). «Думать о множестве вещей одновременно, не запутавшись: параллелизм в действии 1». MIT AI Memo 626 .
  21. Гетц, Брайан (23 ноября 2004 г.). «Параллелизм в JDK 5.0». IBM .
  22. ^ ab "Async in 4.5: Worth the Await – Блог .NET – Главная страница сайта – Блоги MSDN". Blogs.msdn.com . Получено 13 мая 2014 г. .
  23. ^ abc "Асинхронное программирование с Async и Await (C# и Visual Basic)". Msdn.microsoft.com . Получено 13 мая 2014 г. .
  24. Томаш Петричек (29 октября 2010 г.). «Асинхронный C# и F# (I.): Одновременное введение».
  25. ^ Дон Сайм; Томас Петричек; Дмитрий Ломов (21 октября 2010 г.). «Модель асинхронного программирования F #, PADL 2011».
  26. ^ ab Gilad Bracha (октябрь 2014 г.). «Поддержка асинхронности языка Dart: Фаза 1».
  27. ^ ab "PEP 0492 – Сопрограммы с синтаксисом async и await".
  28. ^ Кэндзиро Таура; Сатоши Мацуока; Акинори Ёнэдзава (1994). «ABCL/f: полиморфный типизированный параллельный объектно-ориентированный язык будущего – его разработка и реализация». В трудах семинара DIMACS по спецификации параллельных алгоритмов, номер 18 в серии Dimacs по дискретной математике и теоретической информатике . Американское математическое общество. стр. 275–292. CiteSeerX 10.1.1.23.1161 . 
  29. ^ "Dart SDK dart async Completer" .
  30. ^ «Задача».
  31. ^ Стив Декорте (2005). «Io, язык программирования».
  32. ^ "Использование обещаний". Mozilla Developer Network . Получено 23 февраля 2021 г.
  33. ^ "Упрощение асинхронного программирования с помощью async и await". Mozilla Developer Network . Получено 23 февраля 2021 г.
  34. ^ Рич Хики (2009). "changes.txt в 1.1.x из clojure richhickey". GitHub .
  35. ^ «Будущее – язык программирования Kotlin».
  36. ^ Сейф Хариди; Нильс Францен. «Учебник страны Оз». Глобальная библиотека пользователей Mozart. Архивировано из оригинала 14 мая 2011 г. Получено 12 апреля 2011 г.
  37. ^ Выпуск Python 3.2
  38. ^ Выпуск Python 3.5
  39. ^ "Parallelism with Futures". PLT . Получено 2 марта 2012 г.
  40. ^ "class Promise". raku.org . Получено 19 августа 2022 г. .
  41. ^ "Будущее в std::future - Rust". doc.rust-lang.org . Получено 16 декабря 2023 г. .
  42. ^ Общий Лисп Blackbird
  43. ^ Common Lisp Жаждущее будущее2
  44. ^ Lisp in parallel – Библиотека параллельного программирования для Common Lisp
  45. ^ Common Lisp PCall
  46. ^ "Глава 30. Тема 4.0.0" . Получено 26 июня 2013 г.
  47. ^ "Dlib C++ Library #thread_pool" . Получено 26 июня 2013 г. .
  48. ^ "GitHub – facebook/folly: библиотека C++ с открытым исходным кодом, разработанная и используемая в Facebook". GitHub . 8 января 2019 г.
  49. ^ "HPX". 10 февраля 2019 г.
  50. ^ "Слайды тем POCO" (PDF) .
  51. ^ "QtCore 5.0: QFuture Class". Проект Qt. Архивировано из оригинала 1 июня 2013 г. Получено 26 июня 2013 г.
  52. ^ "Морская звезда". Проект Систар . Проверено 22 августа 2016 г. .
  53. ^ "stlab — это продолжающаяся работа лаборатории программных технологий Adobe. Исходные библиотеки Adobe (ASL), библиотеки платформ и новые библиотеки stlab размещены на github". 31 января 2021 г.
  54. Groovy GPars Архивировано 12 января 2013 г. на Wayback Machine
  55. ^ Cujo.js
  56. ^ JavaScript когда.js
  57. ^ Спецификация Promises/A+
  58. ^ обещания
  59. ^ JavaScript MochKit.Async
  60. ^ JavaScript Angularjs
  61. ^ JavaScript node-обещание
  62. ^ "JavaScript Q". Архивировано из оригинала 31 декабря 2018 года . Получено 8 апреля 2013 года .
  63. ^ JavaScript RSVP.js
  64. ^ Библиотека классов JavaScript YUI
  65. ^ YUI JavaScript-класс обещаний
  66. ^ JavaScript-синяя птица
  67. ^ Java JDeferred
  68. ^ Java ParSeq
  69. ^ Objective-C MAFuture GitHub
  70. ^ Objective-C MAFuture mikeash.com
  71. ^ Objective-C RXPromise
  72. ^ ObjC-CollapsingFutures
  73. ^ Objective-C PromiseKit
  74. ^ Objective-C objc-обещание
  75. ^ Objective-C OAPromise
  76. ^ OCaml Ленивый
  77. ^ Будущее Perl
  78. ^ Обещания Perl
  79. ^ Перл Рефлекс
  80. ^ Обещание Perl::ES6
  81. ^ "Promise::XS – Быстрые обещания в Perl – metacpan.org". metacpan.org . Получено 14 февраля 2021 г. .
  82. ^ PHP-реакция/обещание
  83. ^ Встроенная реализация Python
  84. ^ pythonfutures
  85. ^ "Twisted Deferreds". Архивировано из оригинала 6 августа 2020 года . Получено 29 апреля 2010 года .
  86. ^ R пакет будущего
  87. ^ будущее
  88. ^ Параллельный Ruby
  89. ^ Драгоценный камень Ruby Promise
  90. ^ Руби либув
  91. ^ "Ruby Celluloid gem". Архивировано из оригинала 8 мая 2013 года . Получено 19 февраля 2022 года .
  92. ^ Ruby будущий ресурс
  93. ^ ящик futures-rs
  94. ^ Библиотека утилит Twitter
  95. ^ "Swift Async". Архивировано из оригинала 31 декабря 2018 года . Получено 23 июня 2014 года .
  96. ^ Swift FutureKit
  97. ^ Swift Apple GCD
  98. ^ Swift FutureLib
  99. ^ bignerdranch/Отложенный
  100. ^ Томвис/BrightFutures
  101. ^ Белозеров/SwiftCoroutine
  102. ^ tcl-обещание
  103. ^ Решает ли async/await реальную проблему?
  104. ^ "Go language patterns Futures". Архивировано из оригинала 4 декабря 2020 г. Получено 9 февраля 2014 г.
  105. ^ "Go Language Patterns". Архивировано из оригинала 11 ноября 2020 г. Получено 9 февраля 2014 г.

Внешние ссылки