Promise 比你想像的要有名——它来源于外太空体育竞技已经开始时的两个设想。让我来说你两个有关 1960 二十世纪的基本概念是怎样进入你 JS标识符的故事情节。
在已经开始以后,他们须要晓得并行操作方式和触发器操作方式之间的差别。在讨论标识符和实现技术细节以后,举两个范例更适宜认知。
如果你把当今世界和他们在与触发器虚拟可视化时所做的所有人关连起来,差别就显得更容易认知了。想像呵呵他们在他们的匝道上驾车:你正在听录音机,并与旁的旅客聊天。现在你有喷嚏的贪婪。我们都晓得,喷嚏是驾车时最不该反正。尽你所能,你也难以制止他们喷嚏,只好你已经开始喷嚏并站著。在此刻你所做的所有人都不会“暂停”,你会继续以每半小时 65 英哩的速率高速行驶,但同时你会陷于喷嚏的状况:注销、站著。
喷嚏是我所言的并行现实生活当今世界姿势:当你喷嚏时,其它所有人都不关键,因为这是你所做的所有人。
让我用标识符来表明这一点儿。在这儿你能看见他们列印“hello”,sleep一两年,接着列印“bye”。在标识符sleep时,流程中没其它任何人事出现。当这个过程顺利完成后,标识符拒绝执行。这儿要特别强调的一点儿是,标识符sleep时,他们的流程中没出现任何人事。这是两个关键的基本概念,当他们高踞在触发器重构这条坎坷而模糊不清的方向上后,须要谨记这一点儿。
现如今,关上他们的应用程序控制面板并输出 Promise,他们会看见两个 Promise 缺省。他们能resolve两个Promise,也能reject两个Promise。
这是两个支持度非常好的的 API,你现在能在应用程序和 Node.js 中使用,无需 polyfill 也不须要引入额外的js来实现。
你肯定会说:别废话了,说我一些我还不晓得的事吧。但我想说的是,能够使用这个 API 并不是什么新奇的事,新奇的是问他们为什么我今天能用Promise?
这 7 个字母(“Promise”)是怎样如此不稳定地结合在一起却能产生如此有用和强大的价值?
Ryan Dahl 于 2009 年首次提出 Node.js,这是自 Brendan Eich 编写 JavaScript 的初始实现以来 JavaScript 开发的最大转变之一看看日志(温馨警告 – 这个 repo 有点大,花了 30 分钟 🙁 所以我真的不建议你这样做)
查看 repo,他们能看见在 2009 年 6 月,初始提交里出现了 Promises …。让他们看看它是怎样实现的。
哦,在这儿。等等……这不是两个 Promise 实现,这只是两个封装在类似 Promise 接口中的Event Emitter。但这个设想的意图是什么?这个设想是从哪里来的?
起这个名字的渊源是什么?
要翻旧账非常棘手,但是通过广泛使用回溯历史、电子邮件和个人采访,我能够拼凑出从 Promises 的早期起源到 ES2015 规范的合理历史。
这个有关promise 的故事情节已经开始于 1961 年。1961 年,NASA忙于水星外太空计划,发射了人造卫星 1 号后他们飞向外太空赶上了俄罗斯。黑猩猩哈姆刚刚顺利完成一次成功的外太空之旅,并登上了 LIFE 的封面。
好吧……不提黑猩猩了。回到他们要讲的promise…..
61 年,出现了一篇有关 Algol(算法语言的缩写)的论文,并发表在 ACM 上。本文描述了一种编译流程的语句。我不提技术细节了,因为我晓得很多人已经对算法非常熟悉,但本文探讨的关键基本概念归结为“Tunk”这个设想
thunk 是提供地址的编译时优化。当这个 thunk 被执行时,一些值最终将在某个标准位置(或在给定地址)可用。现在为什么这很关键,这意味着什么?这个“thunk”的东西是两个值引用最终将在未来某个时间存储在给定位置的基本概念。这听起来对 Promise 的基本基本概念有些熟悉——这个值最终将代表未来的某个值。
如果他们跳进德洛里安并快进到 1977 年,那么第一部星球大战刚刚发布,并且引入了两个同样关键的基本概念。发表了描述“future”基本概念的论文。
虽然这篇论文是有关处理垃圾收集的方法,但它有一些新颖的设想,这些设想为未来在最终值和远程执行函数的基本概念上的迭代提供了灵感。
在论文摘要中,作者立即提出了对函数参数进行并行赋值的基本概念。这个设想在 JavaScript 当今世界中并不是什么新鲜事,这实际上是两个非常容易识别的基本概念。他们希望同时并行地eval多个东西。就像网络工作者允许他们做的那样……值得注意的是,这是在 1977 年,而不是 2016 年。
,就没什么能制止每个参数值的赋值和运行“并行且不会相互阻塞。现在,你在想我为什么要关心 params,那些只是一些值……但是,如果你把思维从值转变为不同的东西 – 在这儿将 params 认为不仅仅是简单的值,也可能是其它函数和其它“终”值。
1995 年,引入了Joule语言,旨在成为构建分布式系统的新模型。整个模型是使构建分布式系统显得简单。Joule 中的每个姿势都包括从服务器发送和接收消息。“通道”是传递消息的抽象或消息管道。Joule正是通过这种方式为正式的消息模式奠定了基础。这种负责委托触发器通信而不是基于事件的模式的中间人或中继虚拟的设想意味着数据及其流是可预测的,这使我们能将这些虚拟视为传递数据的另一种模式,即使它是触发器的。
些设想的顶点真正分水岭是在 E语言中实现的 Future/Promise 。虽然 E 引入了许多基本概念,但他们只看语言的两个特定子集 – Promise 实现。E 是第两个真正非阻塞的 Promise 实现,因为“远程”顺利完成的工作永远不会阻塞未来的执行。
E还建立了两个词典,如上图所示。通过一些细微的调整,该图以 1:1 的比例映射到今天的 JavaScript promise。该图来自原始 E 语言论文和 Mark Miller 的描述。注意到,它将本地promise和远程promise视为同一件事,就像 Joule 将所有消息视为“通道”一样,future 可能会启用跨多个进程的完全并行执行。E 建立在这些设想的基础上,并将 Promise 的设想视为与实际顺利完成工作的位置无关。有关这个原始设计的最后两个有趣的注释是,虽然在失败状况下的 Promise 的原始名称是“broken”,这在其它语言中是没问题的,但这在 JavaScript 中不能用……。为什么呢?”broken”=> break。 break是JS里的两个保留字,只好改为 .catch 来取代 broken
Python 的 Twisted 框架直接从 E 语言派生了它他们的 future/deferred 实现。这个实现虽然没吸纳到 E 的promise的全部思想,但带来了足够多的基本概念,成为 JS 实现成长的种子。
从这一点儿已经开始,他们将跟随这个故事情节的 JavaScript 轨迹——须要注意的是,其它语言的实现方向是并行运行的,并且在某些方面在这段时间内更“正确”。
我能找到的第两个遵循 E promise 精髓的 JS 实现是在 MochiKit (从MochiBot抽离))中找到的。 当我联系Bob Ippolito 询问他从哪里得到这个实现设想的灵感时,他说他直接从 Python 的 Twisted 库中移植的。
他跟我解释移植此功能的合理理由(因为它确实经受住了时间的考验),他们的交流很开心。
MochiBot 使用了很多 AJAX 风格的调用,而 Deferred 比直接使用 XMLHttpRequest 更容易使用
正如他指出的那样……
因为今天的 fetch API 提供了两个返回 Promise 的 HTTP 接口,所以这不是两个坏主意。
请记住,这是在 2005 年……在我已经开始认真编程以后,这个直到今天仍然很麻烦的触发器问题在当时就已经被感觉到和处理了。
在2006 年(或者可能更早,因为我认为提交日期与从 SVN 批量导入的日期有关),Dojo Toolkit 团队checked 了deferredRequest。所以我猜在这种情况下,Dojo 不一定是第两个这样做,……但他们确实很早就这样做了。
对互联网进一步搜索,有关 Dojo 有了更多有趣的发现。2007 年,Alex Russell 将Deferred 的设想与 deferredRequest分离。在 2006 年的listserve 帖子中,Alex 还提到了延迟请求的灵感来自 Mochikit 和 Python 的 Twisted 实现。
2009 年初是promise使用激增的时刻,从小众走向主流。虽然你可能对Q有点熟悉,但有两个名为Waterken Q的早期版本,它对现代 Q 和 when.js 等较新的库有很多语法相似性。实现自我标识为“用于与 JSON 资源可视化的简洁且富有表现力的 API”
2009 年,Kris Zyp 提议在 CommonJS 邮件中添加 Promises 作为官方 API。对许多人来说,这是 Promises 在规范中落地的已经开始。在他的帖子中,他指出了 Waterken Q 的影响
2009年9 月,Kris Kowal(也在 Promise API 组)将 waterken Q 的设想与 E 的设计和目标一起创建了流行的 Q 库。
8 天后,Dojo已经开始了探讨,从 deferred 的设想转变为 Promise 的设想。
2010年12 月,deferreds 被引入到 jQuery 核心标识符,但还没作为 API 对外公开。Promise 接口最终在 $.ajax API 中公开——当时任何人编写 JavaScript 的人对此肯定记忆深刻。
这个简单的 API 把XMLHttpRequest的让人诟病的难用之处包装了起来,同时引入了“done”的基本概念作为两个有趣的副作用。
请记住,这段时间网络正在快速发展,HTML5 即将到来,并且越来越多的新网络 API 正在发布:Webcrypto、fetch、IndexedDB、localStorage。这些 API 中的每两个都必须弄清楚怎样处理触发器操作方式。有些人选择使用类似 Promise 的接口,有些人选择使用回调,有些人选择使用事件发射器。当将不同的 Promise 实现添加到组合中时,这种脱节显得特别复杂
Promise 规范很简单,但缺乏统一的测试套件使得不同的实现不能一起工作。Paul Chavard率先提出了为 Promise 规范实现引入test suite 的设想。Paul向 Ember 创建了两个pull request,以引入 Promise test suite。
Domenic Denicola 认识到实现的缺陷,在 Ember PR 登陆的那天继续创建了 Promise A+ 文档和测试套件。
他的测试套件和规范导致了 50 多个兼容的 Promise 实现,这对社区来说是两个巨大的胜利,因为这意味着你不必担心不同的 async / deferred / promise 实现。只要实现是 A+,就晓得它将在 .then 级别兼容。这儿的胜利是向 TC39 表明,社区围绕 Promises 的设想达成了共识…。而几年前,这个设想被认为过于深奥而难以普及。
由于社区的支持和最终须要其它触发器操作方式,例如 ES2015(当时称为 ES6)中的模块加载,Promises 被快速跟踪并落地,这要感谢 Domenic Denicola。
所以他们在 2020 年使用的 API 是始于1961 年的设想和未来工作的最终结合,跨越多个编程语言和时代。
特别感谢 Kris Kowal、Mark Miller、Rebecca Murphey、Alex Russell 和 Domenic Denicola 帮助重建此时间线。还要感谢 Karl Horky 的编辑帮助。
作者 Sam Saccone,译者 若愚,原文:https://samsaccone.com/posts/history-of-promises.html