技术实践

Promise 和 Async await

当谈到在 Javascript 中密切相关的概念 Promises 和 Async await 时,人们总是感到困惑,甚至在看了教程或阅读了

羊先生的头像
羊先生2021.10.12 · 6 分钟阅读 · 44 阅读
Promise 和 Async await封面
文章正文还需 6 分钟

当谈到在 Javascript 中密切相关的概念 Promises 和 Async await 时,人们总是感到困惑,甚至在看了教程或阅读了大量关于它们的文章之后。好吧,不要担心,因为我向你保证,如果你仔细阅读这篇文章,你会更好地理解它们是什么以及它们是如何工作的。

image.png

验证同步还是异步

js 复制代码
let numbers = [1, 2, 4, 7, 3, 5, 6]
numbers.sort((a, b) => a - b)
console.log(numbers); // [ 1, 2, 3, 4, 5, 6, 7 ]

函数 sort ()有一个匿名箭头函数,作为一个回调函数对数组进行排序,我们在控制台上打印它。注意,箭头函数在控制台前运行,因此我们得到了排序数组,但是如果你用 setTimeout 对相同的数组排序,那么控制台将打印未排序的数组:

js 复制代码
let numbers = [2, 1, 4, 7, 3, 5, 6]
// numbers.sort((a, b) => a - b)
setTimeout((a,b) => {
    numbers.sort((a,b) => a-b)
    // console.log(numbers)
}, 0)
console.log(numbers); // [2, 1, 4, 7, 3, 5, 6]

这是因为异步代码在所有同步代码执行完毕之后运行,这就是为什么尽管 setTimeout 有0等待时间,但它仍然在 console.log 之后运行,尽管 console.log 在代码中后来才出现

只要异步操作很少,回调对于异步编程是有用的,但是如果你想异步运行更多的函数,那么回调就会变成一团糟!

js 复制代码
const url1 = 'https://fakeapi.com/1/'
const url2 = 'https://fakeapi.com/2/'
const url3 = 'https://fakeapi.com/3/'

function callback(url,msg){
// calls api here
console.log(msg)
}
setTimeout(() => {
    callback('first message')
    setTimeout( () => {
        callback('second message')
        setTimeout( () => {
            callback('third message')
        },0)
    }, 0)
},0) 
// first message
// second message
// third message

Promise 对象可以由 Promise ()构造函数生成,该构造函数接受一个 executor 函数,该函数接受两个函数作为参数: 一个 resolve 函数和一个 reject 函数。这就是你做出新承诺的方式:

js 复制代码
const aPromise = new Promise( (resolve, reject) => {
  resolve("promise resolved")
})

aPromise.then(( value) => {
  console.log(value)
})
.catch( (error) => {
  console.log(error)
})

这个“ executor”函数根据 Promise 的值调用 resolve 或 reject 函数来终止,这意味着执行函数运行一些代码,如果 Promise 得到解析,则调用 resolve 函数; 如果 Promise 抛出错误,则调用 reject 函数。

js 复制代码
aPromise.then(( value) => {
  return value = 'changed value'
})
.then((value) => {
  console.log(value)
})
image.png

我们通常在进行 api 调用时使用这些方法。让我们使用 fetch API 从 PokeAPI 获取 Pokemon 的列表。获取()。函数返回一个 Promise,这样我们就可以将。然后()及。Catch ()方法:

js 复制代码
const url = 'https://pokeapi.co/api/v2/pokemon?limit=100&offset=200'
fetch(url)
  .then((response) => {
    response.json()
  })
  .then((data) => {
   console.log(data)
  })

这样我们就可以得到100个带有其他属性的口袋妖怪列表。承诺确实让我们的生活变得更加容易,但是我们的代码看起来仍然有很多的链接方法。Asyncawait 使得这个过程更加简洁,并且允许我们编写看起来非常像我们的老式同步代码的异步代码。

Async await 异步等待

用异步等待来代替。然后()及。在处理 Promises 时 catch ()。我们需要分配一个 ASYNC 函数,然后在其中,我们可以在要执行的异步操作之前使用 await 关键字(这意味着我们认为可能需要更长时间的代码)。像这样:

js 复制代码
const loadData = async () {
    const url = 'https://jsonplaceholder.com/todos/1/'
    const res = await fetch(url)
    const data = res.json()
    console.log(data)
}
loadData()

try-catch block 试接块

就像。Catch ()块,我们在 Promises 中使用,在 Async 使用 try-catch 处理错误。这使得代码更加清晰和有组织。

js 复制代码
const loadData = async () {
try{
    const url = 'https://jsonplaceholder.com/todos/1/'
    const res = await fetch(url)
    const data = await res.json()
    console.log(data)
  }
catch(err){
   console.error(err)
 }
}

Promise.all()

如果您需要向一个 API 发出多个请求,并且采用异步等待的方法,那么所有请求返回数据所需的时间总和就是这个时间。假设每个请求需要1秒,对于3个请求,如果我们只是等待每个请求,我们将不得不等待3秒:

js 复制代码
/* Wrong approach */
const loadData = async() {
  try(){
    const url = 'https://jsonplaceholder.com/todos/1/'
    const url2 = 'https://jsonplaceholder.com/todos/2/'
    const url3 = 'https://jsonplaceholder.com/todos/3/'

    const res = await fetch(url)
    const res2 = await fetch(url2)
    const res3 = await fetch(url3)
    const data = await res.json()
    const data2 = await res2.json()
    const data3 = await res3.json()
    return  [data, data2, data3]
   }
   catch(err){
    console.error(err)
   }
}

通过 Promise.all () ,每个请求彼此并行运行,从而得到更快的响应

js 复制代码
/* Right approach */
const loadData = async() {
  try(){
    const url = 'https://jsonplaceholder.com/todos/1/'
    const url2 = 'https://jsonplaceholder.com/todos/2/'
    const url3 = 'https://jsonplaceholder.com/todos/3/'
    const results = await Promise.all([
      fetch(url),
      fetch(url2),
      fetch(url3)
     ])
    const dataPromises = results.map( res => res.json())
    const finalData = await Promise.all(dataPromises)
    return finalData
   }
   catch(err){
    console.error(err)
   }
}

( async() => {
const data = await loadData()
console.log(data)
})()