【译】JavaScript中的Promises

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

你有没有在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对象,就可以验证这点。


image.png


打印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 allowed
  return 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 allowed
  return 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)


image.png


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


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


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


image.png


打印出来是“黑森林蛋糕”还是“没有蛋糕”的信息,取决于你传入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 customer
  chargeCustomer(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 customer
  chargeCustomer(customer)
    .then(/* Add to database */)
    .catch(err => console.log(err))
})


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


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


// Callback based code
app.post('/buy-thing', (req, res) => {
  const customer = req.body
  chargeCustomer(customer, (err, charge) => {
    if (err) throw err
    // Second operation: Add to database
    addToDatabase(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.body
  chargeCustomer(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.body
  chargeCustomer(customer, (err, charge) => {
    if (err) throw err
    addToDatabase(customer, (err, document) => {
      if (err) throw err
      sendEmail(customer, (err, result) => {
        if (err) throw err
        // Tells frontend success message.
        res.send('success!')
      })
    })
  })
})


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


app.post('/buy-thing', (req, res) => {
  const customer = req.body
  chargeCustomer(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。😮


相关文章
|
1月前
|
缓存 JavaScript 前端开发
掌握现代JavaScript异步编程:Promises、Async/Await与性能优化
本文深入探讨了现代JavaScript异步编程的核心概念,包括Promises和Async/Await的使用方法、最佳实践及其在性能优化中的应用,通过实例讲解了如何高效地进行异步操作,提高代码质量和应用性能。
|
7月前
|
前端开发 JavaScript 编译器
深入解析JavaScript中的异步编程:Promises与async/await的使用与原理
【4月更文挑战第22天】本文深入解析JavaScript异步编程,重点讨论Promises和async/await。Promises用于管理异步操作,有pending、fulfilled和rejected三种状态。通过.then()和.catch()处理结果,但可能导致回调地狱。async/await是ES2017的语法糖,使异步编程更直观,类似同步代码,通过事件循环和微任务队列实现。两者各有优势,适用于不同场景,能有效提升代码可读性和维护性。
|
前端开发 算法 JavaScript
JavaScript专项算法题(6):Promises
Promises期约 挑战1 问题: 让我们从复习异步函数开始吧!使用setTimeout,在1000ms后打印字符串“Hello!”。 题解: 1 2 3 4 5 6 7 8 9 // Challenge 1 function sayHello() { // ADD CODE HERE setTimeout(() => console.log('Hello!'), 1000) } // Uncomment the line below when ready sayHello(); // should log "Hello" after 1000ms 挑战2 问题: 创建一
|
前端开发 JavaScript API
JavaScript中Promises/A+规范的实现
Promises是一种异步编程模型,通过一组API来规范化异步操作,这样也能够让异步操作的流程控制更加容易。
JavaScript中Promises/A+规范的实现
|
JavaScript 前端开发 API
异步编程之Javascript Promises 规范介绍
什么是 Promises Promises是一种关于异步编程的规范,目的是将异步处理对象和处理规则进行规范化,为异步编程提供统一接口。   传统的回调函数 说到JavaScript的异步编程处理,通常我们会想到回调函数,如下面的代码: getFileAsync("1.
1009 0