diff --git a/05-JavaScript基础:异步编程和Ajax/01-单线程和异步.md b/05-JavaScript基础:异步编程和Ajax/01-单线程和异步.md index 535956a..2d27e06 100644 --- a/05-JavaScript基础:异步编程和Ajax/01-单线程和异步.md +++ b/05-JavaScript基础:异步编程和Ajax/01-单线程和异步.md @@ -22,31 +22,41 @@ JavaScript 语言和执行环境是**单线程**。即同一时间,只能处 - 异步任务:不进入主线程、而是进入**任务队列**(Event Queue)的任务。只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。 +代码举例: + ```js -console.log('同步任务'); +console.log('同步任务1'); setTimeout(() => { console.log('异步任务'); }, 1000); + +console.log('同步任务2'); ``` -比如上面的代码里:第一行代码是同步任务,会**立即执行**;定时器里的回调函数是异步任务,需要等 1 秒后才会执行。 +打印结果是: -比如说,网络图片的请求,就是一个异步任务。前端如果同时请求多张网络网络图片,谁先请求完成就让谁先显示出来。 +``` +同步任务1 +同步任务2 +异步任务 +``` -假如网络图片的请求做成同步任务,那就会出大问题,所有图片都得排队加载,如果第一张图片未加载完成,就得卡在那里,造成阻塞,导致其他图片都加载不出来。页面看上去也会很卡顿,这肯定是不能接受的。 +代码解释:第一行代码是同步任务,会**立即执行**;定时器里的回调函数是异步任务,需要等 1 秒后才会执行。假如定时器里的代码是同步任务,那需要等待1秒后,才能执行最后一行代码`console.log('同步任务2')`,也就是造成了主线程里的同步任务阻塞,这不是我们希望看到的。 + +比如说,网络图片的请求,就是一个异步任务。前端如果同时请求多张网络网络图片,谁先请求完成就让谁先显示出来。假如网络图片的请求做成同步任务,那就会出大问题,所有图片都得排队加载,如果第一张图片未加载完成,就得卡在那里,造成阻塞,导致其他图片都加载不出来。页面看上去也会很卡顿,这肯定是不能接受的。 ### 前端使用异步的场景 -什么时候需要**等待**,就什么时候用异步。 +什么时候需要**等待**,就什么时候用异步。常见的异步场景如下: -- 定时器:setTimeout(定时炸弹)、setInterval(循环执行) +- 1、定时器:setTimeout(定时炸弹)、setInterval(循环执行) -- 事件绑定(比如说,按钮绑定点击事件之后,用户爱点不点。我们不可能卡在按钮那里,什么都不做。所以,应该用异步) +- 2、事件绑定(比如说,按钮绑定点击事件之后,用户爱点不点。我们不可能卡在按钮那里,什么都不做。所以,应该用异步) -- 网络请求(含接口请求):ajax 请求、网络图片加载 +- 3、网络请求(含接口请求):ajax 请求、网络图片加载 -- ES6 中的 Promise +- 4、ES6 中的 Promise 现在的大部分软件项目,都是前后端分离的。后端生成接口,前端请求接口。前端发送 ajax 请求,向后端请求数据,然后**等待一段时间**后,才能拿到数据。这个请求过程就是异步任务。 @@ -61,11 +71,6 @@ js 中常见的接口调用方式,有以下几种: 下一篇文章,我们重点讲一下接口调用里的 Ajax,然后在 ES6 语法中学习 **Promise**。在这之前,我们需要先了解同步任务、异步任务的事件循环机制。 -### 多次异步调用的顺序 - -- 多次异步调用的结果,顺序可能不同步。 - -- 异步调用的结果如果**存在依赖**,则需要通过回调函数进行嵌套。 ### 事件循环机制(重要) @@ -80,6 +85,13 @@ js 中常见的接口调用方式,有以下几种: - 当主线程的任务执行完毕之后,此时主线程处于空闲状态,于是会去读取 Event Queue 中的任务队列,如果有任务,则进入到主线程去执行。 +### 多次异步调用的顺序 + +- 多次异步调用的结果,顺序可能不同步。 + +- 异步调用的结果如果**存在依赖**,则需要通过回调函数进行嵌套。 + + ## 定时器:代码示例 掌握了上面的事件循环原理之后,我们来看几个例子。 @@ -162,7 +174,8 @@ setTimeout(() => { 上面的案例中,异步任务需要等待 1004 毫秒之后,才会从 Event Table 进入到 Event Queue。这在面试中也经常被问到。 ## 异步任务举例 -### 例1:加载图片 + +### 例 1:加载图片 ```js // 加载图片的异步任务 @@ -193,8 +206,31 @@ loadImage( ); ``` +### 例 2:定时器计时,移动 DOM 元素 -### 例2:定时器计时 +```js +// 函数封装:定义一个定时器,每间隔 delay 毫秒之后,执行 callback 函数 +function myInterval(callback, delay = 100) { + let timeId = setInterval(() => callback(timeId), delay); +} + +myInterval((timeId) => { + // 每间隔 500毫秒之后,向右移动 .box 元素 + const myBox = document.getElementsByClassName('box')[0]; + const left = parseInt(window.getComputedStyle(myBox).left); + myBox.style.left = left + 20 + 'px'; + if (left > 300) { + clearInterval(timeId); + + // 每间隔 10 毫秒之后,将 .box 元素的宽度逐渐缩小,直到消失 + myInterval((timeId2) => { + const width = parseInt(window.getComputedStyle(myBox).width); + myBox.style.width = width - 1 + 'px'; + if (width <= 0) clearInterval(timeId2); + }, 10); + } +}, 200); +``` diff --git a/05-JavaScript基础:异步编程和Ajax/02-Ajax入门和发送http请求.md b/05-JavaScript基础:异步编程和Ajax/02-Ajax入门和发送http请求.md index 2cf268f..4f44c29 100644 --- a/05-JavaScript基础:异步编程和Ajax/02-Ajax入门和发送http请求.md +++ b/05-JavaScript基础:异步编程和Ajax/02-Ajax入门和发送http请求.md @@ -58,6 +58,76 @@ Ajax:Asynchronous Javascript And XML(异步 JavaScript 和 XML)。它并 (5)服务端响应,获取返回的数据。 +## XMLHttpRequest 对象详解 + +我们在上一段讲解了使用 XMLHttpRequest 对象的五个步骤。本段,我们讲一下注意事项。 + +### 发送请求 + +发送请求的方法: + +```javascript +open(method, url, async); +``` + +参数解释: + +- method:请求的类型;GET 或 POST + +- url:文件在服务器上的位置 + +- async:true(异步)或 false(同步) + +另外还有个方法:(仅用于 POST 请求) + +```javascript +send(string); +``` + +### POST 请求时注意 + +如果想让 像 form 表单提交数据那样使用 POST 请求,就需要使用 XMLHttpRequest 对象的 setRequestHeader()方法 来添加 HTTP 头。然后在 send() 方法中添加想要发送的数据: + +```javascript +xmlhttp.open('POST', 'ajax_test.php', true); + +xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + +xmlhttp.send('name=smyhvae&age=27'); +``` + +### onreadystatechange 事件 + +注册 onreadystatechange 事件后,每当 readyState 属性改变时,就会调用 onreadystatechange 函数。 + +readyState:(存有 XMLHttpRequest 的状态。从 0 到 4 发生变化) + +- 0: 请求未初始化 + +- 1: 服务器连接已建立 + +- 2: 请求已接收 + +- 3: 请求处理中 + +- 4: 请求已完成,且响应已就绪 + +status: + +- 200: "OK"。 + +- 404: 未找到页面。 + +在 onreadystatechange 事件中,**当 readyState 等于 4,且状态码为 200 时,表示响应已就绪**。 + +### 服务器响应的内容 + +- responseText:获得字符串形式的响应数据。 + +- responseXML:获得 XML 形式的响应数据。 + +如果响应的是普通字符串,就使用 responseText;如果响应的是 XML,使用 responseXML。 + ## 手写 Ajax ### 手写第一个 Ajax 请求 @@ -223,76 +293,30 @@ myAjax('a.json', (res) => { ![](http://img.smyhvae.com/20180228_1605.gif) +### Ajax 多个接口的嵌套请求(重要) -## XMLHttpRequest 对象详解 +我们在做异步任务的时候,经常会涉及到多个接口的嵌套请求。比如说,接口 1 请求完成后,需要根据接口 1 的数据请求接口 2;接口 2 请求完成后,需要根据接口 3 的数据请求接口 3,以此类推。 -我们在上一段讲解了使用 XMLHttpRequest 对象的五个步骤。本段,我们讲一下注意事项。 +需求描述: -### 发送请求 +- 请求接口 1,根据用户名获取用户 id -发送请求的方法: +- 请求接口 2,根据用户 id 获取用户的年龄、性别等信息。 -```javascript -open(method, url, async); +代码实现思路: + +```js +myAjax('http://localhost:8888/php/user.php?name=千古', (userInfo) => { + // 根据第一个接口返回的 userInfo.id,继续请求第二个接口 + myAjax(`http://localhost:8888/php/houdunren.php?id=${userInfo['id']}`, (res) => { + console.log(response); + }); +}); ``` -参数解释: +我们在实战开发中,经常会涉及到接口请求之间的**依赖**:需要上一个接口请求返回的数据,来发送本次请求。这种场景经常遇到,需要记住。 -- method:请求的类型;GET 或 POST - -- url:文件在服务器上的位置 - -- async:true(异步)或 false(同步) - -另外还有个方法:(仅用于 POST 请求) - -```javascript -send(string); -``` - -### POST 请求时注意 - -如果想让 像 form 表单提交数据那样使用 POST 请求,就需要使用 XMLHttpRequest 对象的 setRequestHeader()方法 来添加 HTTP 头。然后在 send() 方法中添加想要发送的数据: - -```javascript -xmlhttp.open('POST', 'ajax_test.php', true); - -xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - -xmlhttp.send('name=smyhvae&age=27'); -``` - -### onreadystatechange 事件 - -注册 onreadystatechange 事件后,每当 readyState 属性改变时,就会调用 onreadystatechange 函数。 - -readyState:(存有 XMLHttpRequest 的状态。从 0 到 4 发生变化) - -- 0: 请求未初始化 - -- 1: 服务器连接已建立 - -- 2: 请求已接收 - -- 3: 请求处理中 - -- 4: 请求已完成,且响应已就绪 - -status: - -- 200: "OK"。 - -- 404: 未找到页面。 - -在 onreadystatechange 事件中,**当 readyState 等于 4,且状态码为 200 时,表示响应已就绪**。 - -### 服务器响应的内容 - -- responseText:获得字符串形式的响应数据。 - -- responseXML:获得 XML 形式的响应数据。 - -如果响应的是普通字符串,就使用 responseText;如果响应的是 XML,使用 responseXML。 +但这种层层嵌套的代码,会导致**回调地域**的问题,也不利于维护。我们在后续的 ES6 章节中,会讲解 Promise,它是一种更优雅的异步任务解决方案。 ## jQuery 中的 Ajax @@ -302,14 +326,18 @@ JQuery 作为最受欢迎的 js 框架之一,常见的 Ajax 已经帮助我们 ```javascript $.ajax({ - url: '01.php', //请求地址 - data: 'name=fox&age=18', //发送的数据 + url: 'https://xxx.com/getUserInfo.php', // 接口的请求地址 + data: 'name=fox&age=18', // 请求参数 type: 'GET', //请求的方式 - success: function (argument) {}, // 请求成功执行的方法 + success: function (argument) { + // 接口请求成功时调用 + console.log('接口请求成功'); + }, beforeSend: function (argument) {}, // 在发送请求之前调用,可以做一些验证之类的处理 error: function (argument) { - console.log(argument); - }, //请求失败调用 + // 接口请求失败时调用 + console.log('接口请求失败'); + }, }); ``` @@ -332,15 +360,17 @@ $.ajax({ $(function () { $('#btn').click(function () { $.ajax({ - url: 'data.php', + url: 'https://xxx.com/getUserInfo.php', // 接口的请求地址 dataType: 'text', + data: 'name=fox&age=18', // 请求参数 type: 'get', success: function (data) { + console.log('接口请求成功'); alert(data); - //$("#showInfo").html(data); + // $("#showInfo").html(data); }, - error: function (e) { - console.log(e); + error: function (err) { + console.log('接口请求失败:' + err); }, }); }); @@ -370,3 +400,7 @@ echo $text; 扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外: ![](https://img.smyhvae.com/20200102.png) + +``` + +```