介绍
JavaScript 的 Promise 可能难以理解。因此,我想写下我对 Promise 的理解方式。
理解 Promise
简而言之,Promise 是:
“想象你是一个孩子。你妈妈承诺下周给你买一部新手机。”
在下周到来之前,你不知道是否能得到那部手机。你妈妈可能真的会给你买一部全新的手机,或者她不会。
这就是一个承诺。一个承诺有三种状态。它们是:
- Pending(待定):你不知道是否能得到那部手机
- Fulfilled(已完成):妈妈很高兴,她给你买了一部全新的手机
- Rejected(已拒绝):妈妈不高兴,她没有给你买手机
创建一个 Promise
让我们将这个例子转换为 JavaScript。
代码本身相当直观。
以下是通常的承诺语法:
消费承诺
现在我们有了承诺,让我们来消费它:
让我们运行这个例子看看结果!
演示: https://jsbin.com/nifocu/1/edit?js,console
Promise链
Promise是可链式调用的。
假设你,作为孩子,承诺你的朋友,一旦你妈妈给你买了新手机,你就会向他们展示。
这是另一个承诺。让我们来写一下!
备注:我们可以通过以下方式简化上述代码:
让我们将这些承诺串联起来。你,作为孩子,只能在willIGetNewPhone
承诺完成后开始showOff
承诺。
这就是如何链式调用承诺的方法。
承诺是异步的
承诺是异步的。让我们在调用承诺前后分别记录一条消息。
预期的输出顺序是什么?你可能会预期:
1. before asking Mom
2. Hey friend, I have a new black Samsung phone.
3. after asking mom
然而,实际的输出顺序是:
1. before asking Mom
2. after asking mom
3. Hey friend, I have a new black Samsung phone.
你不会在等待妈妈的承诺(新手机)时停止玩耍。这就是我们所说的异步:代码会运行而不阻塞或等待结果。任何需要等待承诺才能继续的代码都放在.then
中。
以下是完整的ES5示例:
ES5, ES6/2015, ES7/Next 中的承诺
ES5 – 主流浏览器
该演示代码在包含 Bluebird 承诺库的情况下,在 ES5 环境中(所有主流浏览器 + NodeJs)可运行。这是因为 ES5 本身不支持承诺。另一个著名的承诺库是由 Kris Kowal 开发的 Q。
ES6 / ES2015 – 现代浏览器, NodeJs v6
演示代码即开即用,因为ES6原生支持Promise。此外,借助ES6的功能,我们可以进一步简化代码,使用箭头函数以及const
和let
。
以下是完整的ES6代码示例:
注意,所有的var
都被替换为const
。所有的function(resolve, reject)
都被简化为(resolve, reject) =>
。这些改动带来了一些好处。
ES7 – Async/Await
ES7引入了async
和await
语法。它使得异步语法更易于理解,无需使用.then
和.catch
。
用ES7语法重写我们的示例:
何时使用Promise
为何我们需要Promise?在Promise出现之前,世界是怎样的?在回答这些问题之前,让我们回归基础。
普通函数与异步函数对比
来看这两个示例。两个示例都执行了两个数字的加法:一个使用普通函数进行加法,另一个则是远程加法。
普通函数加法示例
异步函数加法示例
如果使用普通函数进行加法,你会立即得到结果。然而,当你发起远程调用以获取结果时,你需要等待,无法立即得到结果。
你无法确定是否能得到结果,因为服务器可能宕机、响应缓慢等。你不想整个流程在等待结果时被阻塞。
调用API、下载文件和读取文件是一些常见的异步操作。
异步调用并不一定需要使用Promise。在Promise出现之前,我们使用回调函数。回调函数是你得到返回结果时调用的函数。我们修改前面的例子,使其接受一个回调函数。
后续的异步操作
我们不再一次只加两个数字,而是想加三次。在普通函数中,我们会这样做:
这是使用回调函数的样子:
示例:https://jsbin.com/barimo/edit?html,js,console
由于深度嵌套的回调,这种语法对用户不太友好。
避免深度嵌套回调
Promise可以帮助你避免深度嵌套的回调。让我们看看同一个例子的Promise版本:
通过承诺,我们用 .then
展平回调。从某种程度上看,这显得更整洁,因为没有回调嵌套。使用 ES7 的 async
语法,你可以进一步优化这个示例。
可观察对象
在你决定使用承诺之前,有一种新兴技术可以帮助你处理异步数据,称为 可观察对象
。
让我们看看用可观察对象编写的同一个演示。在这个例子中,我们将使用 RxJS 作为可观察对象的实现。
可观察对象能实现更多有趣的功能。例如,只需一行代码,delay
就可以延迟功能执行 3秒
,或者重试功能,让你可以重试调用一定次数。
你可以在这里阅读我的一篇关于RxJs的文章。
结论
熟悉回调函数和Promise至关重要。理解并运用它们。暂时不必担心Observable。根据具体情况,这三种技术都可以在你的开发中发挥作用。
Source:
https://www.digitalocean.com/community/tutorials/understanding-javascript-promises