发布于 2026-01-06 0 阅读
0

异步 JavaScript 速成课程(第二部分)简介 Promise Async/Await 发送 HTTP 请求 API 项目代码 index.html app.js 结论

异步 JavaScript 速成课程(第二部分)

介绍

承诺

异步/等待

发出 HTTP 请求

API项目

代码

index.html

app.js

结论

介绍

欢迎回来!

以下是我们第二部分将要介绍的内容。

  • 承诺
  • 异步和等待
  • 发出 HTTP 请求
  • API项目

让我们从上次中断的地方继续。首先,我们要摆脱回调地狱。

承诺

什么是承诺?

承诺

JavaScript 中的 Promise 就像现实世界中的“承诺”一词一样。当我们做出承诺时,有两种可能的结果:承诺被履行(已解决)或承诺被打破(已拒绝)。

同样,JavaScript 中的 Promise 对象有 3 种状态:

待处理:初始状态,既未解决也未拒绝。

已解决:表示操作已成功完成。

已拒绝:表示操作失败。

图像

定义: JavaScript 中的 Promise 被定义为一个表示异步操作最终完成(或失败)的对象。

哇!那我们来仔细分析一下。

  • Promise 是一个对象。
  • 它常用于异步操作中。
  • Promise 对象由异步函数返回,这些对象最初可能没有值,但将来某个时候会具有值。

让我们通过一个虚拟函数来理解这一点。

const fakeRequest = (url) => {
    return new Promise((resolve, reject) => {
        const rand = Math.random();
        setTimeout(() => {
            if (rand < 0.7) {
                resolve('YOUR FAKE DATA HERE');
            }
            reject('Request Error!');
        }, 1000)
    })
}
Enter fullscreen mode Exit fullscreen mode

这个函数本质上是在模拟远程服务器的响应。当我们向服务器请求数据时,服务器可能需要几秒钟才能完成或拒绝我们的请求。从某种意义上说,服务器是在承诺会向我们返回数据。

当函数被调用时,它会创建一个 Promise。函数可以保留(resolve)该 Promise 并返回数据,也可以拒绝(reject)该 Promise 并抛出错误。

该函数生成一个介于 0 和 1 之间的随机数。如果该数字小于 0.7,则使用 resolve 函数以数据作为参数来解决此承诺;否则,将使用 reject 函数拒绝此承诺并抛出错误。

既然我们已经创造了承诺,我们该如何使用它呢?

  • 我们在函数调用中使用.then(),.catch()来消费 promise。

。然后

  • 我们传递一个回调函数,.then该函数会在 Promise 被解析时执行。
fakeRequest("fakeurl").then((data)=>{
 console.log(data)
})
Enter fullscreen mode Exit fullscreen mode
  • 如果 Promise 被解析,则“data”参数将包含“你的虚假数据”。

。抓住

  • 如果承诺被拒绝,我们需要捕获该错误。

抓住

  • 我们附加了.catch()后置条件.then(),以捕获任何被拒绝的承诺。
fakeRequest("fakeurl").then((data)=>{
 console.log(data)
}).catch((error){
 console.log(error)
})
Enter fullscreen mode Exit fullscreen mode
  • 如果抛出任何错误,'error' 参数的值将为'请求错误!'。

  • 简单来说,.thenis 代表解决,.catchis 代表拒绝。

回调函数和 Promise 的主要区别在于,在 Promise 中,你将回调(.then 和 .catch)附加到返回的 Promise 对象,而在回调函数中,你将回调作为参数(success 和 failure)传递给函数。

你还记得我们之前用回调函数实现的彩虹函数吗?
让我们用 Promise 来重新实现它。

const delayedColorChange = (color, delay) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            document.body.style.backgroundColor = color;
            resolve();
        }, delay)
    })
}
Enter fullscreen mode Exit fullscreen mode

现在,如果我们想让颜色依次显示,只需.then按顺序链式调用即可。由于每个 Promise 中的代码.then只有在前一个 Promise 解析后才会执行,因此我们可以同步(依次)执行多个异步任务(例如更改背景颜色)。

类似这样的:

delayedColorChange('red', 1000)
    .then(() => delayedColorChange('orange', 1000))
    .then(() => delayedColorChange('yellow', 1000))
    .then(() => delayedColorChange('green', 1000))
    .then(() => delayedColorChange('blue', 1000))
    .then(() => delayedColorChange('indigo', 1000))
    .then(() => delayedColorChange('violet', 1000))
Enter fullscreen mode Exit fullscreen mode
  • 首先会调用 delayedColorChange 函数来更改颜色为红色。
  • 当它完成并解决承诺“.then”时,将调用 delayedColorChange 来更改颜色为橙色,依此类推。

让我们来看一个真实案例。

  • 有时您可能需要连续多次调用 API 或数据库来获取数据。
  • 假设你需要调用两个 API 'A' 和 'B',但是要调用 'B',你需要传递从 'A' 获取的数据。
  • 所以需要先解决“A”,对吧?之后.then才能调用“B”。

  • 我们可以使用我们的fakeRequest函数来发出这些虚假的 API 请求。

  • 以下是我们的做法:

fakeRequest('urlA')
    .then((dataa) => {
        console.log("(urlA) worked")
        return fakeRequestPromise('UrlB')
    })
    .then((datab) => {
        console.log("(urlB) worked")
    })
    .catch((err) => {
        console.log("OH NO, A REQUEST FAILED!!!")
        console.log(err)
    })

Enter fullscreen mode Exit fullscreen mode
  • 这样做之所以有效,是因为.then只有在 Promise 被解析后才会运行。然后我们调用 urlB API。
  • 接下来,.then附加了一个用于处理 urlB 的代码。
  • 如果其中任何一个请求失败(承诺被拒绝),它们就会直接传递到.catch.
  • 如果我们希望在 urlB 之后进行更多 API 调用,我们只需不断返回函数并链式调用即可.then
fakeRequest('urlA')
    .then((dataa) => {
        console.log("(urlA) worked !!!")
        return fakeRequestPromise('urlB')
    })
    .then((datab) => {
        console.log("(urlB) worked !!!")
        return fakeRequestPromise('urlC')
    })
    .then((datac) => {
        console.log("(urlC) worked !!!")
    })
    .catch((err) => {
        console.log("OH NO, A REQUEST FAILED!!!")
        console.log(err)
    })
Enter fullscreen mode Exit fullscreen mode

这样做的目的是为了简化代码。我们可以看到,与使用回调函数相比,使用 Promise 实现的 Rainbow 函数和模拟请求函数都显得简洁得多。

异步/等待

  • Async 和 Await 是程序员所说的语法糖。
  • 它本质上是一种更简便的 Promise 使用方式。

## 异步
async 关键字可以放在函数前面。

const greeting = async ()=>{
   return "Nice to meet you!!"
}
Enter fullscreen mode Exit fullscreen mode
  • 使用 async 关键字意味着该函数现在总是返回一个 Promise。
  • 无论函数返回什么类型的值,它始终会被包装在一个 Promise 中。
  • 既然该函数返回的是一个 Promise,我们就可以使用以下方式调用它.then
greeting().then((data)=>{
 console.log(data)
})
Enter fullscreen mode Exit fullscreen mode
  • 这将打印出“很高兴见到你!!”
  • 因此,我们不需要显式地解决承诺。

## 等待

  • await 关键字会暂停异步函数中的代码,直到 Promise 被解析。
  • await 只能在异步函数中使用。
  • 如果在异步函数之外使用 Await 关键字,则会出现语法错误。
 let result = await promise;
Enter fullscreen mode Exit fullscreen mode
  • 让我们调用我们的 Rainbow 函数delayedColorChange(),但这次使用 async 和 await。

  • 首先,我们创建一个异步函数,该函数将调用不同颜色对应的函数。

const rainbow = async ()=>{

}
Enter fullscreen mode Exit fullscreen mode
  • 让我们选择delayedColorChange红色。
  • 红色部分结束后,函数应该返回“已打印”。
const printRainbow = async ()=>{
  delayedColorChange('red', 1000)
  return "Printed"
}
Enter fullscreen mode Exit fullscreen mode
  • 但是,如果我们这样执行,该函数不会等待红色显示出来,而是会立即打印“已打印”。
  • 因此,我们必须要求该printRainbow函数等待直到delayedColorChange('red', 1000)其承诺得到解决。
    等待

  • 为此,我们将await关键字放在前面delayedColorChange

  • 这将要求对方printRainbow等待承诺得到解决。

const printRainbow = async ()=>{
  await delayedColorChange('red', 1000)
  return "Printed"
}
Enter fullscreen mode Exit fullscreen mode
  • 让我们把剩下的颜色都放进去。
const printRainbow = async ()=>{
  await delayedColorChange('red', 1000)
  await delayedColorChange('orange', 1000)
  await delayedColorChange('yellow', 1000)
  await delayedColorChange('green', 1000)
  await delayedColorChange('blue', 1000)
  await delayedColorChange('indigo', 1000)
  await delayedColorChange('violet', 1000)
  return "Printed"
}

printRainbow().then((data)=>(
  console.log(data)
))
Enter fullscreen mode Exit fullscreen mode
  • 所有颜色显示完毕后,控制台会打印“已打印”。

为了处理错误,我们可以使用 try-catch 块。

const printRainbow = async ()=>{
   try{
        await delayedColorChange('red', 1000)
        return "Printed"
   } 
    catch{
        console.log("error")
    }
}
Enter fullscreen mode Exit fullscreen mode

我们可以将其与该函数的回调版本进行比较。

//Promises (Async and Await)
const printRainbow = async ()=>{
  await delayedColorChange('red', 1000)
  await delayedColorChange('orange', 1000)
  await delayedColorChange('yellow', 1000)
  await delayedColorChange('green', 1000)
  await delayedColorChange('blue', 1000)
  await delayedColorChange('indigo', 1000)
  await delayedColorChange('violet', 1000)
  return "Printed"
}

//Callbacks
delayedColorChange('red', 1000, () => {
    delayedColorChange('orange', 1000, () => {
        delayedColorChange('yellow', 1000, () => {
            delayedColorChange('green', 1000, () => {
                delayedColorChange('blue', 1000, () => {
                    delayedColorChange('indigo', 1000, () => {
                        delayedColorChange('violet', 1000, () => {
                             //This function will be empty since 
                             //we want to end the 
                            //color change
                        })
                    })
                })
            })
        })
    })
});

Enter fullscreen mode Exit fullscreen mode

我们可以看到,它更加干净整洁,结构也更加紧凑。

发出 HTTP 请求

  • 现在让我们来了解异步 JavaScript 的最后一块拼图。
  • 我们知道如何使用 Promise 和回调来处理来自 API 的响应。
  • 现在,我们应该发出真正的 HTTP 请求,而不是使用fakerequest()函数来伪造它。

请求头

HTTP 标头本质上是客户端和服务器之间传递附加信息的一种方式。 我们可以看到,通过标头传递的关于请求的附加信息以键值对的形式存在。
请求头

HTTP 请求的类型

  • GET:用于从资源中“获取”数据。
  • POST:用于向特定目标发送数据。
  • PUT:用于更新现有数据。
  • 删除:用于删除数据。

我们主要会使用GETrequest,因为我们想从 API 获取 JSON 数据。

在 Web API 部分,我们通过浏览器向 Pokémon API 发出的请求是一个GET请求,因为我们从 API 服务器“获取”图像数据。

但是,要在我们自己的网站中使用来自 Web API 的数据,我们必须通过 JavaScript 发出请求。

可以通过 JavaScript 以不同的方式异步向这些 Web API 发出请求。

  • XMLHttpRequest
  • 获取 API
  • Axios

我们的网页将使用 Axios。不过,我们也先来了解一下 XHR 和 fetch API。

XMLHttpRequest

  • XHR 是使用 JavaScript 发送请求的原始方式。
  • 这是一个浏览器API。
  • 由于这种方法使用了回调函数,并且不支持 Promise,因此并非首选方法。

基本语法如下:

var xhttp = new XMLHttpRequest();//1
xhttp.onload = function() {//4
  const data = JSON.parse(this.responseText);
};

xhttp.onerror = function() { // only triggers if the request couldn't be made at all
  alert(` Error`);
};
xhttp.open("GET", "filename", true);//2
xhttp.send();//3
Enter fullscreen mode Exit fullscreen mode
  1. 我们创建一个 xhttp 对象
  2. xhttp.open("GET", "url", true);打开请求时,需指定:请求类型、要请求的 URL、是否要异步请求。
  3. 我们用它xhttp.send()来发送请求。
  4. 我们设置了一个方法onload,该方法会返回responseText请求中的数据。

获取 API

与旧式的 XMLHttpRequest 相比,Fetch 可以更轻松地发出 Web 请求和处理响应。

它使用 Promise,避免了回调地狱,也无需记住 XMLHttpRequest 的复杂 API,因此是一个更简单的 API。

句法

fetch('http://example.com/movies.json')
  .then(response => response.json())
  .then(data => console.log(data));
Enter fullscreen mode Exit fullscreen mode

我们将 URL 传递给 fetch 函数,该函数会返回一个包含响应的 Promise。然而,这只是一个 HTTP 响应,而不是实际的 JSON 数据。要获取 JSON 内容,我们使用.json响应的 `getJSON()` 方法。最后,我们将数据打印到控制台。


Axios

  • Axios 是一个基于 Promise 的 HTTP 客户端,适用于浏览器和 Node.js。
  • 这是对fetch API的改进。

安装

Axios 可以通过 CDN 或 node.js 添加:

使用 CDN

我们可以将以下脚本标签附加到 html 文档中我们自己的 js 文件上方,以使用 Axios。

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
Enter fullscreen mode Exit fullscreen mode

使用 npm

$ npm install axios
Enter fullscreen mode Exit fullscreen mode

用法

获取请求

发起 GET 请求非常简单,只需编写 `.getRequest()` 即可axios.get(url)。为了处理响应,我们使用 `.getResponse()` .then(),并且.catch()由于 axios 使用 Promise,因此
我们不需要.then像 fetch API 那样链式调用其他 API。

axios.get('API URL')
  .then(function (response) {
    // handle success
    console.log(response);
  })
  .catch(function (error) {
    // handle error
    console.log(error);
  })
Enter fullscreen mode Exit fullscreen mode

我们还可以使用 async/await。

const main = async ()=>{
    try{
        let result = await axios.get("url")
        return result
    }catch{
        console.log("request failed");
    }
}
Enter fullscreen mode Exit fullscreen mode

让我们使用 Axios 向 GitHub API 发出请求:

axios.get('https://api.github.com/users/mapbox')
  .then((response) => {
    console.log(response.data);
    console.log(response.status);
    console.log(response.statusText);
    console.log(response.headers);
  });

// logs:
// => {login: "mapbox", id: 600935, node_id: "MDEyOk9yZ2FuaXphdGlvbjYwMDkzNQ==", avatar_url: "https://avatars1.githubusercontent.com/u/600935?v=4", gravatar_id: "", …}

// => 200

// => OK

// => {x-ratelimit-limit: "60", x-github-media-type: "github.v3", x-ratelimit-remaining: "60", last-modified: "Wed, 01 Aug 2018 02:50:03 GMT", etag: "W/"3062389570cc468e0b474db27046e8c9"", …}

Enter fullscreen mode Exit fullscreen mode

我们还可以使用 Axios 发出其他请求。

  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.options(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])

有些参数用方括号括起来,因为它们是可选的。

API项目

现在,是时候把我们目前所学到的所有知识整合起来了。

我们将要建造的是:
图像

这个项目是关于什么的?

这个项目是科尔特·斯蒂尔(Colt Steele)的网页开发者训练营课程的一部分
它是一个网站,使用tvmaze API根据用户的输入显示电视节目。

代码

以下是已完成项目的链接:TvShowApi

index.html

  • 我们创建一个表单,并给它添加一个类,search-form以便稍后在 JavaScript 中使用。
  • 添加文本输入框以获取数据。
  • 用于提交表单的提交按钮。
    <form class="search-form" >
    <input type="text" class="form-control" id="search" placeholder="Search Tvshows">
    <input type="submit" class="form-control mt-2" id="sub" value="Submit">
    </form>
Enter fullscreen mode Exit fullscreen mode

我们将从 API 获取的图像将显示在此 div 元素内。

<div class="shows">   
</div>
Enter fullscreen mode Exit fullscreen mode

app.js

使用查询选择器选择所有必需元素。

let input = document.querySelector("#search");
let showdiv = document.querySelector('.shows')
let searchForm = document.querySelector('.search-form')
Enter fullscreen mode Exit fullscreen mode

  • 我们创建了一个事件监听器,每次表单提交时都会异步发出 axios 请求。
  • 从 axios 获取数据后,我们将其传递给一个createImages函数,该函数用于显示我们从 API 返回的不同节目的图像。
searchForm.addEventListener("submit",async (e)=>{ //e is a event object 
    e.preventDefault(); //Prevents form from refreshing the page.
    if(input.value != ""){ // Checking if the input is empty.
        try {
            let result = await axios.get(`http://api.tvmaze.com/search/shows?q=${input.value}`)
            createImages(result.data)//result.data is an array
            console.log(result); // You can look at the result from the api in the console
        }
        catch (error) {
            console.log(error);
        }
    }
})
Enter fullscreen mode Exit fullscreen mode

以下是 API 返回的响应:
图像


下面这个 createImages 函数用于根据 API 数据创建图像。

const createImages = (shows)=>{//shows is an array
    for(show of shows){ 
        if (show.show.image) { // checking if there is an image for the current show
            let image = document.createElement('img')
            image.src = show.show.image.medium // show.show.image.medium contains the url of the image
            showdiv.append(image) //we attach the images to an empty div that we created in html
        }
    };
}
Enter fullscreen mode Exit fullscreen mode

下图显示了我们在函数中用于获取图像 URL 的路径createImages
图像

结论

我希望读完本文后,您能更好地理解异步 JavaScript 这幅拼图的不同组成部分。

接下来,你需要做的就是在自己的代码中尝试不同的概念。你也可以尝试文章中提供的不同代码片段,并观察得到的结果。

如果您觉得这篇文章有用,请在评论区告诉我。

下次见 :)

文章来源:https://dev.to/chinmaymhatre/crash-course-in-asynchronous-javascript-part-2-29jn