专业的编程技术博客社区

网站首页 > 博客文章 正文

「翻译」JS可视化学习之七:Promise、事件循环和异步1

baijin 2024-10-11 10:47:22 博客文章 12 ℃ 0 评论

承诺最悲哀的不在于无法兑现,而在没有兑现时对于彼此的伤害。如果上天能够给我一个再来一次的机会,我会把承诺和猫装进盒子里。 - 量子态的Promise


请注意上面这张图,Promise的那些事,将在这个图上缓缓展开。

译者术语解释

Promise:可以对比理解中文词语承诺,某些期望憧憬会发生的。一般都可实现,也会出现不能实现的。

Resolve:处理Promise。表明工作正常,当前Promise得到处理,会调用后续环节函数,并把处理的数据传过去。

Reject:拒绝Promise。表明有些异常或者条件没有达到,会调用拒绝函数,一般建议组装Error对象作为入参传递过去,并跳转到catch中运行。

原文比较长,所以这里会分成三部分来讲解:Promise、事件循环和异步。


你是否曾经不得不去处理那些...没有按照你预期运行的JS代码?也许是函数不可预知的随机执行了,或者是延迟运行了。这给了你一个机会来使用ES6引进的一个很酷的新功能:Promises

我多年来的求知欲得到了回报,不眠之夜再次给了我制作动画的时间。是时候谈谈Promise了:你为什么要使用它们,它们是如何在“幕后”工作的,我们如何才能用最现代化的方式把它们写出来?

如果您还没有阅读我之前关于JavaScript事件循环的文章,那么先读一下,可能会很有用!这里我将再次介绍一下事件循环,针对调用堆栈、Web API和队列做一些简单介绍,但这次我们还将介绍一些额外的令人兴奋的特性。

如果你已经熟悉了Promise,这里有一些捷径可以帮你节省宝贵的滑动时间。


介绍

在编写JavaScript时,我们经常要处理依赖于其他任务的任务!假设我们获取一个图像,压缩它,然后使用滤镜,最后保存它。

我们需要做的第一件事,就是得到我们想要编辑的图像,getImage函数可以处理这件事!只有成功加载该图像后,我们才能将它传递给resizeImage函数。成功调整图像大小后,我们希望在applyFilter函数中对图像应用一个滤镜。在图像被压缩并且添加了一个滤镜之后,我们希望保存图像,并让用户知道一切都工作正常!

最后,我们可能会得到如下代码:

getImage('/image.png', (image, err) => {
  if (err) throw new Error(err)
  compressImage(image, (compressedImage, err) => {
    if (err) throw new Error(err)
    applyFilter(compressedImage, (filteredImage, err) => {
      if (err) throw new Error(err)
      saveImage(compressedImage, (res, err) => {
        if (err) throw new Error(err)
        console.log("Successfully saved image!")
      })
    })
  })
})

嗯...注意到了吗?虽然...好吧,代码看起来不太好的样子。我们最终得到许多依赖于前一个回调函数的嵌套回调函数。这通常被称为回调地狱,因为我们最终会有大量的嵌套回调函数,这使得代码很难阅读!

幸运的是,我们现在有Promise来拯救我们!让我们来看看什么是Promise,以及在这种情况下它们是如何帮助我们的!


Promise语法

ES6引入了Promise。在许多教程中,您会看到如下内容:

Promise是值的占位符,该值可以在将来某个时间处理或者拒绝

嗯...这个解释,相当于没有解释。事实上,这只会让我觉得Promise是一个诡异的,不明确的,不可预测的魔法。让我们看看Promise到底是什么。

我们可以使用Promise构造函数来创建Promise,构造函数接受一个回调函数作为入参。超酷,让我们试试!

等等,哇,刚刚返回了什么玩意?

Promise是一个包含状态([[PromiseStatus]])和值([[PromiseValue]])的对象。在上面的例子中,您可以看到[[PromiseStatus]]的值是“pending”,[[PromiseValue]]的值是“undefined”。

别担心-您永远都不会与这个对象有什么交集,您甚至无法访问[[PromiseStatus]][[PromiseValue]]属性!但是,在使用Promise时,这些属性的值很重要。


PromiseStatus的值,也就是状态,可以是以下三个值之一:

? fulfilledPromise已经得到处理了。一切都很顺利,在Promise中没有出现错误。

? rejectedPromise被拒绝了。啊,出了点问题...

? pendingPromise既没有处理,也没有被拒绝(还没有),Promise仍然在等待结果。

听起来不错,但Promise的状态什么时候是“pending”、“fulfilled”或者“rejected”呢?为什么status的地位如此重要?

在上面的例子中,我们仅仅将简单的回调函数 ()=>{} 传递给Promise构造函数。但是,这个回调函数实际上接收两个参数。第一个参数的值,通常称为resolveres,是在Promise应当得到处理时调用的方法。第二个参数的值,通常称为rejectrej,是在Promise应该被拒绝、出现问题时调用的方法。

让我们看看,当调用resolvereject方法时的输出信息!在示例中,res表示resolve方法,rej表示reject方法。

棒极了!我们终于知道如何摆脱“pending”状态和"undefined"值!如果调用resolve方法,promise的状态为“fulfilled”;如果调用rejected方法,则promise的状态为“rejected”。

Promise的值,即[[PromiseValue]]的值,是我们传递给resolvedrejected方法的参数的值。

有趣的是,我让 jakearchibald 校对这篇文章时,他实际上指出Chrome中有一个bug,当前显示的状态是“resolved”而不是“fulfilled”。多亏了Mathias Bynens,它在Canary中被修复了!


好了,现在我们知道了如何控制不明确的Promise对象。但是我们可以用它来做什么呢?

在介绍部分,我展示了一个例子,在这个例子中,我们获取一个图像,压缩它,应用一个滤镜,然后保存它!最终,这变成了一个嵌套的回调地狱。

幸运的是,Promise可以帮助我们解决这个问题!首先,让我们重写整个代码块,让每个函数返回一个Promise

如果图像被加载并且一切正常,那么让我们用加载的图像来处理这个Promise!否则,如果在加载文件时某个地方出现了问题,让我们用发生的错误来拒绝Promise

function getImage(file) {
  return new Promise((res, rej) => {
    try{
      const data = readFile(file)
      resolve(data)
    } catch(err) {
      reject(new Error(err))
    }
  })
}

让我们看看在终端运行时会发生什么!

酷!像我们预期的那样,返回了一个包含解析过的数据的Promise

但是...现在怎么办?我们不关心整个承诺对象,我们只关心这个数据的值!幸运的是,有一些内置的方法可以获得Promise的值。对于Promise,我们可以附加3个方法:

.then(): 在处理Promise后调用。

.catch(): 在拒绝Promise后调用。

.finally(): 始终被调用,无论Promise得到处理还是被拒绝。

getImage(file)
  .then(image => console.log(image))
  .catch(error => console.log(error))
  .finally(() => console.log("All done!"))

.then方法接收传递给resolve方法的值。

.catch方法接收传递给被reject方法的值。

最后,我们在没有获得整个Promise对象的情况下获取到了Promise处理的值!我们现在可以对这个值做任何事情。


小提示,如果你确定一个Promise总是会得到处理或者总是被拒绝,你可以使用Promise.resolve 或者 Promise.reject,并把您要拒绝或处理Promise的值传给它们!

在接下来的示例中会经常看到这种语法。


getImage示例中,我们不得不嵌套多个回调来运行它们。幸运的是,.then处理程序可以在这一块帮助我们!

.then 回调本身返回的结果就是一个Promise值,这意味着我们可以链接任意多个.then:前一个then回调的结果将作为参数传递给下一个then回调!

getImage示例中,我们可以将多个then回调链接起来,以便将处理后的图像传递给下一个函数!这样我们最终得到了一个干净的then链,而不是多个回调的嵌套。

getImage('./image.png')
  .then(image => compressImage(image))
  .then(compressedImage => applyFilter(compressedImage))
  .then(filteredImage => saveImage(filteredImage))
  .then(res => console.log("Successfully saved image!"))
  .catch(err => throw new Error(err))

完美!这个语法看起来已经比嵌套回调好多了。


翻译来源:https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke#tasks

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表