你有没有在JavaScript中遇到过promises并想知道它们是什么?它们为什么会被称为promises呢?它们是否和你以任何方式对另一个人做出的承诺有关呢?

此外,你为什么要使用promises呢?与传统的JavaScript操作回调(callbacks)相比,它们有什么好处呢?

在本文中,你将学习有关JavaScript中promises的所有内容。你将明白它们是什么,怎么去使用它们,以及为什么它们比回调更受欢迎。

所以,promise是什么?

promise是一个将来会返回值的对象。由于这种未来的东西,Promises非常适合异步JavaScript操作。

如果你不明白异步JavaScript意味着什么,你可能还不适合读这篇文章。我建议你回到关于callbacks这篇文章了解后再回来。

通过类比会更好地解析JavaScript promise的概念,所以我们来这样做(类比),使其概念更加清晰。

想象一下,你准备下周为你的侄女举办生日派对。当你谈到派对时,你的朋友,Jeff,提出他可以提供帮助。你很高心,让他买一个黑森林(风格的)生日蛋糕。Jeff说可以。

在这里,Jeff告诉你他会给你买一个黑森林生日蛋糕。这是约定好的。在JavaScript中,promise的工作方式和现实生活中的承诺一样。可以使用以下方式编写JavaScript版本的场景:

// jeffBuysCake is a promise
const promise = jeffBuysCake('black forest')

你将学习如何构建jeffBuysCake。现在,把它当成一个promise

现在,Jeff尚未采取行动。在JavaScript中,我们说承诺(promise)正在等待中(pending)。如果你console.log一个promise对象,就可以验证这点。

打印jeffBuysCake表明承诺正在等待中。

当我们稍后一起构建jeffBuysCake时,你将能够自己证明此console.log语句。

在与Jeff交谈之后,你开始计划下一步。你意识到如果Jeff信守诺言,并在聚会时买来一个黑森林蛋糕,你就可以按照计划继续派对了。

如果Jeff确实买来了蛋糕,在JavaScript中,我们说这个promise是实现(resolved)了。当一个承诺得到实现时,你会在.then调用中做下一件事情:

jeffBuysCake('black forest').then(partyAsPlanned) // Woohoo! ???

如果Jeff没给你买来蛋糕,你必须自己去面包店买了。(该死的,Jeff!)。如果发生这种情况,我们会说承诺被拒绝(rejected)了。

当承诺被拒绝了,你可以在.catch调用中执行应急计划。

jeffBuysCake('black forest').then(partyAsPlanned).catch(buyCakeYourself) // Grumble Grumble... #*$%

我的朋友,这就是对Promise的剖析了。

在JavaScript中,我们通常使用promises来获取或修改一条信息。当promise得到解决时,我们会对返回的数据执行某些操作。当promise拒绝时,我们处理错误:

getSomethingWithPromise().then(data => {/* do something with data */}).catch(err => {/* handle the error */})

现在,你知道一个promise如何运作了。让我们进一步深入研究如何构建一个promise

构建一个promise

你可以使用new Promise来创建一个promise。这个Promise构造函数是一个包含两个参数 – resolvereject 的函数。

const promise = new Promise((resolve, reject) => {/* Do something here */
})

如果resolve被调用,promise成功并继续进入then链式(操作)。你传递给resolve的参数将是接下来then调用中的参数:

const promise = new Promise((resolve, reject) => {// Note: only 1 param allowedreturn resolve(27)
})// Parameter passed resolve would be the arguments passed into then.
promise.then(number => console.log(number)) // 27

如果reject被调用,promise失败并继续进入catch链式(操作)。同样地,你传递给reject的参数将是catch调用中的参数:

const promise = new Promise((resolve, reject) => {// Note: only 1 param allowedreturn reject('???')
})// Parameter passed into reject would be the arguments passed into catch.
promise.catch(err => console.log(err)) // ???

你能看出resolvereject都是回调函数吗??

让我们练习一下,尝试构建jeffBuysCake promise。

首先,你知道Jeff说他会买一个蛋糕。那就是一个承诺。所以,我们从空promise入手:

const jeffBuysCake = cakeType => {return new Promise((resolve, reject) => {// Do something here})
}

接下来,Jeff说他将在一周内购买蛋糕。让我们使用setTimeout函数模拟这个等待七天的时间。我们将等待一秒,而不是七天:

const jeffBuysCake = cakeType => {return new Promise((resolve, reject) => {setTimeout(()=> {// Checks if Jeff buys a black forest cake}, 1000)})
}

如果Jeff在一秒之后买了个黑森林蛋糕,我们就会返回promise,然后将黑森林蛋糕传递给then

如果Jeff买了另一种类型的蛋糕,我们拒接这个promise,并且说no cake,这会导致promise进入catch调用。

const jeffBuysCake = cakeType => {return new Promise((resolve, reject) => {setTimeout(()=> {if (cakeType- = 'black forest') {resolve('black forest cake!')} else {reject('No cake ?')}}, 1000)})
}

让我们来测试下这个promise。当你在下面的console.log记录时,你会看到promise正在pedding(等待)。(如果你立即检查控制台,状态将只是暂时挂起状态。如果你需要更多时间检查控制台,请随时将超时时间延长至10秒)。

const promise = jeffBuysCake('black forest')
console.log(promise)

打印jeffBuysCake表明承诺正在等待中。

如果你在promise链式中添加thencatch,你会看到black forest cake!no cake ?信息,这取决于你传入jeffBuysCake的蛋糕类型。

const promise = jeffBuysCake('black forest').then(cake => console.log(cake)).catch(nocake => console.log(nocake))

打印出来是“黑森林蛋糕”还是“没有蛋糕”的信息,取决于你传入jeffBuysCake的(参数)。

创建一个promise不是很难,是吧??

既然你知道什么是promise,如何制作一个promise以及如何使用promise。那么,我们来回答下一个问题 – 在异步JavaScript中为什么要使用promise而不是回调呢?

Promises vs Callbacks

开发人员更喜欢promises而不是callbacks有三个原因:

  1. Promise减少了嵌套代码的数量
  2. Promise允许你轻松地可视化执行流程
  3. Promise让你可以在链式的末尾去处理所有错误

为了看到这三个好处,让我们编写一些JavaScript代码,它们通过callbackspromises来做一些异步事情。

对于这个过程,假设你正在运营一个在线商店。你需要在客户购买东西时向他收费,然后将他们的信息输入到你的数据库中。最后,你将向他们发送电子邮件:

  1. 向客户收费
  2. 将客户信息输入到数据库
  3. 发送电子邮件给客户

让我们一步一步地解决。首先,你需要一种从前端到后端获取信息的方法。通常,你会对这些操作使用post请求。

如果你使用ExpressNode,则初始化代码可能如下所示。如果你不知道任何NodeExpress(的知识点),请不要担心。它们不是本文的主要部分。跟着下面来走:

// A little bit of NodeJS here. This is how you'll get data from the frontend through your API.
app.post('/buy-thing', (req, res) => {const customer = req.body// Charge customer here
})

让我们先介绍一下基于callback的代码。在这里,你想要向客户收费。如果收费成功,则将其信息添加到数据库中。如果收费失败,则会抛出错误,因此你的服务器可以处理错误。

代码如下所示:

// Callback based code
app.post('/buy-thing', (req, res) => {const customer = req.body// First operation: charge the customerchargeCustomer(customer, (err, charge) => {if (err) throw err// Add to database here})
})

现在,让我们切换到基于promise的代码。同样地,你向客户收费。如果收费成功,则通过调用then将其信息添加到数据库中。如果收费失败,你将在catch调用中自动处理:

// Promised based code
app.post('/buy-thing', (req, res) => {const customer = req.body// First operation: charge the customerchargeCustomer(customer).then(/* Add to database */).catch(err => console.log(err))
})

继续,你可以在收费成功后将你的客户信息添加到数据库中。如果数据库操作成功,则会向客户发送电子邮件。否则,你会抛出一个错误。

考虑到这些步骤,基于callback的代码如下:

// Callback based code
app.post('/buy-thing', (req, res) => {const customer = req.bodychargeCustomer(customer, (err, charge) => {if (err) throw err// Second operation: Add to databaseaddToDatabase(customer, (err, document) => {if (err) throw err// Send email here})})
})

对于基于promise的代码,如果数据库操作成功,则在下一个then调用时发送电子邮件。如果数据库操作失败,则会在最终的catch语句中自动处理错误:

// Promised based code
app.post('/buy-thing', (req, res) => {const customer = req.bodychargeCustomer(customer)// Second operation: Add to database.then(_ => addToDatabase(customer)).then(/* Send email */).catch(err => console.log(err))
})

继续最后一步,在数据库操作成功时向客户发送电子邮件。如果成功发送此电子邮件,则会有成功消息通知到你的前端。否则,你抛出一个错误:

以下是基于callback的代码:

app.post('/buy-thing', (req, res) => {const customer = req.bodychargeCustomer(customer, (err, charge) => {if (err) throw erraddToDatabase(customer, (err, document) => {if (err) throw errsendEmail(customer, (err, result) => {if (err) throw err// Tells frontend success message.res.send('success!')})})})
})

然后,以下基于promise的代码:

app.post('/buy-thing', (req, res) => {const customer = req.bodychargeCustomer(customer).then(_ => addToDatabase(customer)).then(_ => sendEmail(customer) ).then(result => res.send('success!'))).catch(err => console.log(err))
})

看看为什么使用promises而不是callbacks编写异步代码要容易得多?你从回调地狱(callback hell)一下子切换到了链式乐土上?。

一次触发多个promises

promisescallbacks的另一个好处是,如果操作不依赖于彼此,则可以同时触发两个(或多个)promises,但是执行第三个操作需要两个结果。

为此,你使用Promise.all方法,然后传入一组你想要等待的promises。then的参数将会是一个数组,其包含你promises返回的结果。

const friesPromise = getFries()
const burgerPromise = getBurger()
const drinksPromise = getDrinks()const eatMeal = Promise.all([friesPromise,burgerPromise,drinksPromise
]).then([fries, burger, drinks] => {console.log(`Chomp. Awesome ${burger}! ?`)console.log(`Chomp. Delicious ${fries}! ?`)console.log(`Slurp. Ugh, shitty drink ${drink} ? `)})

备注:还有一个名为Promise.race的方法,但我还没找到合适的用例。你可以点击这里去查看。

最后,我们来谈谈浏览器支持情况!如果你不能在生产环境中使用它,那为什么要学习promises呢。是吧?

浏览器支持Promise

令人兴奋的消息是:所有主流浏览器都支持promises!

如果你需要支持IE 11及其以下版本,你可以使用Taylor Hakes制作的Promise Polyfill。它支持IE8的promises。?

结语

你在本文中学到了所有关于promises的知识。简而言之,promises棒极了。它可以帮助你编写异步代码,而无需进入回调地狱。

尽管你可能希望无论什么时候都使用promises,但有些情况callbacks也是有意义的。不要忘记了callbacks啊?。

如果你有疑问,请随时在下面发表评论,我会尽快回复你的。【PS:本文译文,若需作者解答疑问,请移步原作者文章下评论】

感谢阅读。这篇文章是否帮助到你?如果有,我希望你考虑分享它。你可能会帮助到其他人。非常感谢!

后话

原文:https://zellwk.com/blog/js-promises/

文章首发:https://github.com/reng99/blogs/issues/19

同步掘金:https://juejin.im/post/5ccb10455188250f3a050eb5

更多内容:https://github.com/reng99/blogs

下一篇关于 async/await

【译】JavaScript中的Promises相关推荐

  1. [译] JavaScript 中的 CSS:基于组件的样式的未来

    本文讲的是[译] JavaScript 中的 CSS:基于组件的样式的未来, 原文地址:CSS in JavaScript: The future of component-based styling ...

  2. [译]JavaScript中,{}+{}等于多少?

    最近,Gary Bernhardt在一个简短的演讲视频"Wat"中指出了一个有趣的JavaScript怪癖:在把对象和数组混合相加时,会得到一些你意想不到的结果.本篇文章会依次讲解 ...

  3. [译]Javascript中的闭包(closures)

    本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...

  4. [译]JavaScript中的属性:定义和赋值的区别

    原文:http://www.2ality.com/2012/08/property-definition-assignment.html 你知道吗?定义一个属性和为一个属性赋值是有区别的.本文解释了两 ...

  5. [译]Javascript中的mutators

    本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...

  6. Javascript 中 Array.push 要比 Array.concat 快 945 倍

    [译] Javascript 中 Array.push 要比 Array.concat 快 945 倍!

  7. 如何在 JavaScript 中等待函数完成

    目录 JavaScript 中的 Sync 和 Async 在 JavaScript 中使用 回调 来等待一个函数的完成 在 JavaScript 中使用 promises 来等待一个函数的完成 使用 ...

  8. 【译】JavaScript中的Callbacks

    你是否遇到过"callbacks"一词,但是不知道这意味着什么?别着急.你不是一个人.许多JavaScript的新手发现回调也很难理解. 尽管callbacks可能令人疑惑,但是你 ...

  9. [译] ES6+ 中的 JavaScript 工厂函数(第八部分)

    本文讲的是[译] ES6+ 中的 JavaScript 工厂函数(第八部分), 原文地址:JavaScript Factory Functions with ES6+ 原文作者:Eric Elliot ...

最新文章

  1. 清华大学张悠慧:超越冯·诺依曼模型,实现软硬件去耦合的类脑计算(附视频)
  2. php 连接hadoop hdfs,Hadoop HDFS操作
  3. 广域网安全建设的思路和部署
  4. linux 符号链接攻击防御简介
  5. Android源码打包命令
  6. linux学习教程(一)(安装篇)centos7没有安装ifconfig命令的解决方法
  7. SVM支持向量机绘图
  8. python安装依赖失败_python执行安装第三方依赖numpy失败:error: Unable to find vcvarsall.bat...
  9. c语言程序位置式pid算法,增量式与位置式PID算法(C语言实现与电机控制项目)...
  10. 试解析Tomcat运行原理(一)--- socket通讯
  11. oracle按照时间点回退,【Oracle】查看事务回滚的时间
  12. shell脚本作为保证PHP脚本不挂掉的守护进程实例
  13. $stateParams 详解
  14. 通达信指标加密DLL加密解密三个公式源码准确率90%以上超级指标精准买卖绝世指标
  15. chrome浏览器书签同步_如何将Google Chrome浏览器的书签与手机同步
  16. Flutter 利用 FFI,绕过 Android JNI 直接调用 C++ 层!
  17. URL Schemes
  18. 墙裂推荐一个高质量的数据分析学习平台
  19. 综合架构(备份部分)
  20. 磨砺数年,高效PERC技术终迎好时光

热门文章

  1. 给Scrapy添加代理
  2. 关于微信授权登录的用户取消-2的问题
  3. Moblin v2开发环境设置——创建一个新应用程序
  4. arcgis取消投影_ArcGIS中的投影问题
  5. C++右值引用与函数返回值
  6. NBS-Predict:基于脑网络的机器学习预测
  7. 快来打造属于自己的天猫精灵
  8. SSH的远程访问及控制
  9. 基础30讲 第六讲 中值定理
  10. html+canvas 星空背景案例