1. ajax
/*
ajax
+ 前后端交互的一种手段
+ 通过 JS 向服务端发起请求
=> 所有服务端返回的响应都不会直接显示再页面上
=> 而是返回给 js 这个语言
+ 说明: JS 和服务端交互
=> 依赖于浏览器来发送请求
+ ajax
=> a: async
=> j: javascript
=> a: and
=> x: xml
使用方式
1. 找到一个对象能帮我发送 ajax 请求
=> XMLHttpRequest() 内置构造函数
=> 专门创建实例化对象帮你发送 ajax 请求
2. 对本次请求进行一些配置
=> open() 的方法
=> 语法: xhr.open(请求方式, 请求地址, 是否异步)
-> 请求方式: GET POST PUT ...(大小写无所谓)
-> 请求地址: 你要请求哪一个后端位置
-> 是否异步: 选填, 默认是 true, 可以选填 false, 表示同步
3. 把请求发出去
=> send() 方法
=> 语法: xhr.send()
4. 接收响应
=> onload 事件
=> 语法: xhr.onload = function () {}
=> 本次请求结束以后触发(响应成功了以后触发)
=> xhr 里面有一个属性叫做 responseText 就是响应体
*/
console.log('start')
// 1. 创建一个 ajax 实例化对象
const xhr = new XMLHttpRequest()
// 2. 配置本次请求的信息
xhr.open('GET', './server/get.php')
// 3. 把这个请求发送出去
xhr.send()
// 4. 接收结果
xhr.onload = function () {
console.log(JSON.parse(xhr.responseText))
}
console.log('end')
2. ajax 的异步问题
/*
ajax 的异步问题
1. open 的第三个参数可以配置
=> 默认是 true 表示异步
=> 可以选填 false 表示 同步
分析四个步骤
1. 创建 ajax 对象, 同步代码
2. 配置请求信息, 同步代码
3. 发送请求, 异步代码, 当代码执行到这句话的时候, 先把请求发出去
=> 等到响应的过程是异步
4. 事件, 会在满足条件的时候触发
=> 条件: 响应回来
异步执行的时候
console.log('start')
1. 创建 ajax 对象
2. 配置请求信息
3-1. 把请求发出去
4. 绑定事件, 请求完成的事件
console.log('end')
3-2. 响应回到客户端, 触发事件
console.log(响应体)
同步执行的时候
console.log('start')
1. 创建 ajax 对象
2. 配置请求信息
3-1. 把请求发出去(同步), 等到响应回来再继续执行代码
3-2. 响应回到客户端, 不会触发事件, 因为事件还没有绑定
4. 绑定事件, 事件再也不会触发了
console.log('end')
+ 如果想接收到响应, 需要再 send 之前绑定事件
结论:
+ 同步的时候, 事件必须写在 send 之前
+ 异步的时候, 事件写在前面后面无所谓
+ 书写 ajax 的时候, 都要按照 1 2 4 3 的步骤写
*/
console.log('start')
// 1. 创建一个 ajax 实例化对象
const xhr = new XMLHttpRequest()
// 2. 配置本次请求的信息
xhr.open('GET', './server/get.php', false)
// 4. 接收结果
xhr.onload = function () {
console.log(JSON.parse(xhr.responseText))
}
// 3. 把这个请求发送出去
xhr.send() // 发出去, 接收回来响应
console.log('end')
3. ajax 的兼容
/*
ajax 的兼容
+ ajax 不动向下兼容
+ ajax 的兼容有两个部分
1. 创建 ajax 对象
2. 接收响应
创建 ajax 对象的兼容
1. new XMLHttpRequest() 标准浏览器使用
2. new ActiveXObject('Msxml.XMLHTTP') IE 7 8 9
3. new ActiveXObject('Msxml2.XMLHTTP') IE 6
4. new ActiveXObject('Microsoft.XMLHTTP') IE 5.5+
5. 再向下的 IE 不支持 ajax
+ 你用的 IE 11 浏览器, 跑不起来
=> ajax 是基于内核的兼容
接收响应的兼容
+ 再 IE 低版本里面没有 onload 事件
+ 再 IE 低版本只能使用 onreadystatechange 事件来接收响应
=> 再事件里面进行判断
=> xhr.status 再 200 ~ 299 之间
=> xhr.readyState === 4 的时候
=> 正常使用响应体
*/
var xhr = new XMLHttpRequest()
console.log(xhr)
xhr.open('GET', './server/get.php')
xhr.onreadystatechange = function () {
if (xhr.status >= 200 && xhr.status < 300 && xhr.readyState === 4) {
console.log(xhr.responseText)
}
}
xhr.send()
/*
ajax 状态码
+ 状态码
=> 响应状态码: 描述本次请求的状态
=> ajax状态码: 描述 ajax 进行到哪一个步骤了
+ 语法: xhr.readyState
0: 创建 ajax 对象成功
1: 配置请求信息完成
2: 请求发送出去了, 响应报文回到了浏览器
3: 浏览器正在解析响应报文
4: 浏览器解析响应报文成功, 已经可以正常使用 xhr.responseText
*/
// const xhr = new XMLHttpRequest()
// console.log(xhr.readyState)
// xhr.open('GET', './server/get.php')
// console.log(xhr.readyState)
/*
ajax 的状态码改变事件
+ xhr.onreadystatechange = function () {}
*/
// 绑定一个状态码改变事件
// xhr.onreadystatechange = function () {
// console.log(xhr.readyState)
// if (xhr.readyState === 2) {
// console.log('2 响应体 ' , xhr.responseText)
// }
// if (xhr.readyState === 3) {
// console.log('3 响应体 ' , xhr.responseText)
// }
// if (xhr.readyState === 4) {
// console.log('4 响应体 ', xhr.responseText)
// }
// }
// xhr.send()
/*
响应状态码
+ 再 xhr 里面还有一个信息表示 响应状态码
+ xhr.status
+ 描述本次请求的状态
*/
// const xhr = new XMLHttpRequest()
// xhr.open('GET', './server/get1.php')
// xhr.onload = function () {
// console.log(xhr)
// }
// xhr.send()
4. 发送请求
/*
发送一个带有参数的 get 请求
+ GET 请求就是直接再地址栏后面拼接 queryString 方式携带参数
+ open 的第二个参数就是请求地址
+ 我们把要携带给后端的内容通过 open 的第二个参数携带过去
*/
const xhr = new XMLHttpRequest()
xhr.open('GET', './server/get.php?a=100&b=200')
xhr.onload = function () {
console.log(JSON.parse(xhr.responseText))
}
xhr.send()
/*
发送一个带有参数的 post 请求
+ POST 携带参数是在请求体, 不需要再地址栏拼接
=> 数据格式无所谓, 但是要和 content-type 配套
+ send() 的括号里面就是请求体
+ 因为你没有设置 content-type 所以后端不能正常按照 $_POST 的方式解析
=> 设置一下请求头, content-type 设置为 application/x-www-form-urlencoded
=> 语法: xhr.setRequestHeader(key, value)
*/
const xhr = new XMLHttpRequest()
xhr.open('POST', './server/post.php')
xhr.onload = function () {
console.log(JSON.parse(xhr.responseText))
}
// post 请求需要再请求之前设置请求头
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
xhr.send('a=100&b=200')
5. 前后端交互的报错
/*
前后端交互的报错
+ JSON 解析出错了
分析原因
+ 报错: JSON.parse(xxx) 出错了
+ xxx : 哪来的, 响应体, 后端给的
+ 百分百: 后端给的不是 json 格式的数据
+ 检查: 看后端给的到底是什么
1. 把 JSON.parse 去掉, 直接打印 xhr.responseText
2. 打开浏览器 -> network -> 找到你的这个请求 -> 点击 response
*/
const xhr = new XMLHttpRequest()
xhr.open('GET', './server/get.php?a=100&username=guoxiang')
xhr.onload = function () {
console.log(JSON.parse(xhr.responseText))
}
xhr.send()
6. 封装 ajax 操作
/*
封装 ajax 操作
+ 哪些作为参数使用
=> 请求方式
-> type: 默认值 GET
=> 请求地址
-> url: 必填
=> 是否异步
-> async: 默认值 true
=> 是否执行 JSON.parse()
-> dataType: 默认是 'string' 表示不解析, 'json'
=> 传递给后端的数据
-> data: 'a=100&b=200' || { a: 100, b: 200 }
+ 确定一下传递参数的方式
=> function ajax() {}
=> ajax('./xxx.php', null, null, 'json', 'a=100')
=> ajax({ type: 'GET' })
=> 对象的方式传递参数
开始封装
1. 参数验证: 你传递的是不是符合规则
1-1. url 验证, 必填
1-2. type 验证, 可以不传递, 可以是 GET, 可以是 POST, 大小写无所谓
=> 其他的都不行
1-3. async 验证, 可以不传递, 可以是 true 可以是 false
=> 要吗不传递, 要吗就是 boolean
1-4. dataType 验证, 可以不传递, 要吗式 'string' 要吗式 'json'
1-5. data 验证, 可以不传递, 可以是字符串类型, 可以是 object 类型
1-6. success 和 error 验证, 可以不传递, 要吗就得式函数
2. 设置一套默认值
=> 后期的请求发送都是使用默认值发送
=> 设置默认值的时候, 把 data 单独操作一下
=> 判断如果是一个对象, 我把他还原成 字符串的形式再使用
3. 发送请求
=> 按照 _default 里面的内容发送请求
4. 回调函数
=> 目前发送 ajax 只能把结果再控制台打印
=> 我们就把想在请求结束后做的事情包装成一个 盒子, 传递给 ajax 里面
=> 让他再请求成功的时候, 给我把盒子执行掉
*/
ajax({
url: './server/get1.php',
data: { a:100, b:200 },
dataType: 'json',
success: function (xhr) {
console.log('请求成功')
console.log('我想渲染页面, 根据结果判断登录是否成功')
console.log(xhr)
},
error: function (err) {
console.log('请求失败')
console.log(err)
}
})
function ajax(options = {}) {
// options = {} 目的: 为了保证你的 options 是一个对象
// 我执行 options.xxx 的时候不会报错
// options.success 和 error 式两个函数数据类型或者没传递
// 1. 参数验证
// 1-1. 验证 url
if (!options.url) {
throw new Error('url 为必填选项')
}
// 1-2. 验证 type
if (!(options.type == undefined || options.type.toUpperCase() === 'GET' || options.type.toUpperCase() === 'POST')) {
throw new Error('目前只接收 GET 或者 POST 请求方式, 请期待更新')
}
// 1-3. 验证 async
if (!(options.async == undefined || typeof options.async === 'boolean')) {
throw new Error('async 需要一个 Boolean 数据类型')
}
// 1-4. 验证 dataType
if (!(options.dataType == undefined || options.dataType === 'string' || options.dataType === 'json')) {
throw new Error('目前只支持 string 和 json 格式解析, 请期待更新')
}
// 1-5. 验证 data
if (!(options.data == undefined || typeof options.data === 'string' || Object.prototype.toString.call(options.data) === '[object Object]')) {
throw new Error('data 参数只支持 string 和 object 数据类型')
}
// 1-6. 验证 success 和 error
if (!(options.success == undefined || typeof options.success === 'function')) {
throw new Error('success 传递一个函数类型')
}
if (!(options.error == undefined || typeof options.error === 'function')) {
throw new Error('error 传递一个函数类型')
}
// 代码来到这里, 说明 options.success 和 error 肯定是一个 undefined 或者 function
// 2. 设置一套默认值
var _default = {
url: options.url,
// 代码能来到这里, 说名 undefined get post
type: options.type || 'GET',
// 代码能来到这里, 说明 undefined true false
// 三元表达式, 如果你式个 布尔值, 那么就用你的, 否则用 true
async: typeof options.async === 'boolean' ? options.async : true,
// 代码能来到这里, 说明 undefined 'string' 'json'
dataType: options.dataType || 'string',
// 代码能来到这里, 说明 undefined '' {}
data: options.data || '',
// 如果你传递了是一个 function, 就用你传递的, 否则我就给一个默认函数
success: options.success || function () {},
error: options.error || function () {}
}
// 到这里, _default.success 和 error 肯定式一个函数
// 2-2. 单独调整一下 data
// 能来到这里, _default.data 只能是 '' {}
if (typeof _default.data === 'object') {
// 准备一个空字符串
var str = ''
for (var key in _default.data) {
str += key + '=' + _default.data[key] + '&'
}
// 拼接完毕以后, 把最后一位去掉, 从新赋值给 _default.data
_default.data = str.slice(0, -1)
}
// 3. 发送请求
var xhr = creXhr()
// 3-1. 请求地址, 如果是 get 请求 url + '?' + data
// 如果式 post 请求 url
// 判断, 如果是 get 请求, 那么我把 _default.url 修改一下
if (_default.type.toUpperCase() === 'GET' && _default.data) {
_default.url += '?' + _default.data
}
xhr.open(_default.type, _default.url, _default.async)
xhr.onreadystatechange = function () {
if (xhr.status >= 200 && xhr.status < 300 && xhr.readyState === 4) {
// 3-3. 判断 dataType 如果式 json 要解析
if (_default.dataType === 'json') {
// 成功, 不需要打印
// 调用 _default.success()
var res = JSON.parse(xhr.responseText)
// 要吗调用的式你传递进来的函数, 要吗调用的式默认函数
// 调用的如果式默认函数, 那么就相当于什么都没执行
// 如果调用的式你传递进来的函数, 那么你在函数里面写什么就执行什么
_default.success(res)
} else if (_default.dataType === 'string') {
_default.success(xhr.responseText)
}
}
if (xhr.readyState === 4 && xhr.status >= 400) {
_default.error(xhr.status)
}
}
// 3-2. 判断是不是 post 请求
if (_default.type.toUpperCase() === 'POST') {
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
}
xhr.send(_default.data)
}
</script>
<script>
/*
封装 ajax
设计模式: 为了解决特定问题而给出的简洁优化的解决方案
+ 懒惰模式: 多种方案选择一种的方式
+ 例子: 创建 ajax 对象
=> 四种方式
=> 封装的时候要判断
=> 假设: 刚好是第四个可以用
=> 再找个页面你创建两次的时候,
-> 第一次要判断到第四个
-> 第二次还是要判断到第四个
+ 懒惰模式, 第一次的时候, 判断到第四个, 从第二次开始, 不再进行判断
1. 封装创建 xhr 对象
2. 封装 ajax
*/
/*
封装一个函数, 创建 xhr 对象
=> 不需要参数
=> 返回值就是一个可以再本浏览器使用的 xhr 对象
封装过程
1. 把每一种创建方式写成一个函数
=> 依次去调用找个函数
=> 如果不报错, 表示这个方法可以使用
=> 如果 a 函数可以使用, 表示 a 函数里面的代码再当前浏览器可以正常创建
=> 我把 creXhr 重新赋值, 赋值为 a
=> 从此以后, 当你再次调用 creXhr 的时候, 其实就是再调用 a 函数
2. 把四个函数放在一个数组里面
=> 循环遍历这个数组
=> 循环的尝试
3. 准备一个开关
=> 判断数组里面是不是有能执行的
=> 开始的时候式 false
=> 一旦有一个可以使用 变量赋值为 true
*/
// function creXhr() {
// var xhr = null
// // 准备一个变量
// var flag = false
// // 根据各种判断, 来给 xhr 赋值
// var arr = [
// function a() { return new XMLHttpRequest() },
// function b() { return new ActiveXObject('Microsoft.XMLHTTP') },
// function c() { return new ActiveXObject('Msxml.XMLHTTP') },
// function d() { return new ActiveXObject('Msxml2.XMLHTTP') }
// ]
// for (let i = 0; i < arr.length; i++) {
// // arr[i] 式里面的每一个函数
// try {
// xhr = arr[i]()
// // 这里的代码可以执行, 表示 arr[i] 函数里面写的代码就是当前浏览器用的
// creXhr = arr[i]
// flag = true
// break
// } catch (e) {}
// }
// // 判断 flag
// // 如果式 false, 表示数组里面的每一个都不能用
// if (!flag) {
// xhr = '您的浏览器不支持 ajax, 请更换浏览器重试'
// throw new Error(xhr)
// }
// // 返回 xhr
// return xhr
// }
7. 跨域
/*
同源策略
+ **同源策略是浏览器给的一个行为**
+ 当你再发送请求的时候, 会涉及到两个地址
1. 打开当前页面的地址
2. 你要请求的地址
+ 两个地址中的 端口号 域名 传输协议
=> 只要由任意一个不一样, 就是非同源请求
=> 就会触发浏览器的同源策略
=> 不允许你获取这个服务器上的数据
触发了同源策略的请求我们叫做 跨域请求
+ 私人: 请求别人家的服务器
+ 第一:
=> 真实开发环境
=> 页面(html, js, css, 静态资源) 是在一个服务器上
=> 所有的数据, 数据库, 在一个服务上
+ 第二:
=> 真实开发环境
=> 我自己不具备条件, 购买别人家服务器服务
=> 美团: 地图功能
=> 新闻: 买新浪的接口, 腾讯的接口
解决浏览器不允许请求别人家服务器的情况
+ 基于 http 协议
1. jsonp
2. cors
3. 代理
*/
/*
打开页面 http://localhost:80/04_跨域请求.html
请求地址 http://localhost:80/server/test.php
*/
// ajax({
// url: './server/test.php',
// success (res) {
// console.log(res)
// }
// })
/*
打开页面 http://localhost:80/04_跨域请求.html
请求地址 http://127.0.0.1:80/server/test.php
*/
// ajax({
// url: 'http://127.0.0.1:80/server/test.php',
// success (res) {
// console.log(res)
// }
// })
/*
地址
+ 打开页面 localhost/index.html
=> 再页面里面发送一个请求 ajax({ url: './a.php' })
=> 请求地址: localhost/a.php
+ 完整地址
=> 打开页面 http://localhost:80/index.html
=> 请求地址 http://localhost:80/a.php
*/
8. jsonp 跨域
/*
jsonp 跨域
+ 因为浏览器的同源策略, 不允许发送跨域的 ajax 请求
+ 我们使用 jsonp 手法来实现跨域请求
script 标签
+ script 标签可以执行 js 代码
=> script 标签有一个属性叫做 type="text/javascript"
=> 就会把里面的代码当作 js 来解析
=> 当你不写 type 属性的时候, 默认是 text/javascript
+ src 属性
=> src 是引入外部资源的属性
=> 不受同源策略的影响
+ 当上面两个加再一起
=> 只要你引入任何一个内容, 都会被当作 js 代码来解析
jsonp 的核心
+ 利用 script 标签的 src 属性
+ 去向一个非同源的服务器请求数据
+ 只要这个服务器能给我返回一个字符串
+ 我就会把这个字符串当作 js 代码来执行
jsonp 请求数据
+ 要求服务器返回一个 函数名() 这样的字符串
+ 要求提前准备号一个函数
+ 要求前端告诉后端你准备好的函数名是什么
=> 再发送请求的时候, 以参数的形式告诉后端
=> 我准备好的函数名叫做什么
jsonp 常见的面试题
1. jsonp 原理
=> src 不受同源策略影响
=> script 标签会把请求的内容当作 js 代码来执行
2. jsonp 的返回值
=> 字符串, 函数名() 形式的字符串
=> 一段可以执行的 js 代码字符串
3. jsonp 的优缺点
=> 优点
-> 绕开了同源策略, 实现跨域请求
-> 方便, 因为是以 script 标签外部资源的形式请求
=> 缺点
-> 不好做安全防范
*/
9.跨域请求
/*
跨域请求
+ 什么是同源策略
=> **同源策略是浏览器给的机制**
=> 当你再发送一个请求的时候
=> 打开页面的地址和请求地址中, 端口号 传输协议 域名, 有任意一个不一样
=> 就是触发了同源策略
=> 浏览器不允许你请求这个服务器的数据
+ 什么是跨域请求
=> 当触发了同源策略以后, 我还需要获取该服务器的数据
=> 这种请求叫做跨域请求
+ 跨域请求的解决方案
=> 基于 http 协议的跨域方案
1. jsonp
-> 利用了 src 属性不受同源策略的影响
-> 利用了 script 标签会把请求回来的内容当作 js 代码来执行
-> 要求服务器返回一段可以执行的 js 代码( 函数名(数据) )
-> 要求前端提前准备好这个被执行的函数
-> 要求前端以参数的形式把这个被执行函数的函数名传递到后端
2. 代理
-> 利用一个正向代理的机制来实现
-> 任何一台服务器都可以做代理
=> apache 服务器 代理 http 协议的是免费的
=> apache 服务器 代理 https 协议需要证书的
=> 我们配置代理使用 nginx 服务器来配置代理
-> 配置
=> phpstudy 切换到 nginx 服务器
=> 其他选项菜单 -> 打开配置文件 -> nginx.conf
=> 找到当前服务器的 server 标签对, 找到闭合标签的上一行书写代理配置
=> location = /xx {
proxy_pass 地址;
}
-> /xx: 代理标识符, 当你请求 /xx 的时候, nginx 会发现你在请求代理标识符
就会自动帮你转发你的请求到 proxy_pass 后面的地址
-> proxy_pass: 代理目标地址
=> 只要配置文件被修改了, 那么就要重启服务器
3. cors - 跨域资源共享
=> 因为跨域请求, 不是请求发不出来
=> 实际上: 请求已经发送了, 而且到了服务器了, 响应页回到浏览器了
=> 但是浏览器判断了是非同源位置, 不允许你使用服务器给回的数据
=> 由服务器告诉浏览器一个事情, 这个域名我允许请求我的内容
header("Access-Control-Allow-Origin:*");
header("Access-Control-Request-Methods:GET, POST");
header('Access-Control-Allow-Headers:x-requested-with,content-type,test-token,test-sessid');
*/
10.函数防抖
/*
函数防抖
+ 在一个时间节点内, 多次触发同一事件
+ 不需要每一次都执行, 只需要在一个固定时间内, 没有重复操作的时候
+ 执行一次事件
+ 例子: 滚动条事件
-> 在我一直快速滚动的时候, 你不要触发
-> 等到我的滚动结束了, 停下来以后你在触发
+ 实现:
-> 准备一个定时器, 把你要做的事情放在定时器里面做
-> 当你在 300 ms 内做同一个行为的时候, 把定时器关闭掉
-> 只有当你停下来的一瞬间, 不会再继续关闭定时器的时候, 才会触发事件
*/
var timer = null
window.onscroll = function () {
clearInterval(timer)
timer = setTimeout(() => {
console.log('触发')
}, 300)
}
/*
不停的触发滚动事件
1. 关闭定时器, 没得关代码白执行
timer 赋值为 300 ms 以后执行
2. 300ms 以内当我触发第二次的时候
关闭之前的哪个定时器, 之前那一次 300ms 以后的就不会执行了
从新设置了一个 300ms 的定时器
*/
11.函数节流
/*
函数节流
+ 再固定时间内, 重复触发同一事件
+ 我们再固定时间内, 只有第一次时候执行, 后面的每一次都不再执行了
+ 知道我们设定的固定时间到达以后, 再次允许执行下一个事件
+ 例子: 滚动
=> 随着滚动一直触发事件
=> 只要你添加一个定时器就可以了
-> 定时器的作用, 只是为了模拟一个多少时间以后
=> 准备一个开关, 事件里面的代码根据开关来决定是不是执行
*/
// 做一个开关
var flag = true
window.onscroll = function () {
if (flag === false) return
// 能来到这里, 说明 flag 是 true
flag = false
console.log('我执行了')
setTimeout(() => {
// 再次把开关打开
flag = true
}, 300)
}
/*
随着滚动一直再触发 scroll 事件
1. flag === true, 关闭开关, 执行要执行的代码, 设置定时器
2. 300ms 以内, 再次触发事件的时候, flag === false
3. 300ms 以内, 再次触发事件的时候, flag === false
4. 300ms 以后, 开关变成开启, 再次执行事件, flag === true, 执行要执行的代码, 设置定时器
*/
12.回调函数 callback
/*
回调函数 callback
+ 什么是回调函数
=> 定义: 把 A 函数当作参数传递到 B 函数内部
再 B 函数内部以形参的方式调用 A 函数
=> 这种函数的调用方式, 我们叫做回调函数(callback)
+ 回调函数的缺点:
=> 回调地狱
=> 不停的再一个回调函数里面去进行第二个回调函数的操作
=> 就是代码没有可读性和可维护性
+ 为什么需要回调函数
=> 异步
=> 因为我要在异步的末尾或者中间做一些事情
*/
/*
需求1:
发送请求: 请求 a.php 文件
能返回两个结果, 一个a 一个 b
需求2:
发送请求: 请求 b.php 文件
需要携带参数: 第一个 a.php 文件返回的两个数字
需求3:
发送请求: 请求 c.php 文件
需要携带参数: 第一个 b.php 文件返回的两个数字
*/
let obj = null
// 多出现在开发中
ajax({
url: './server/a.php',
dataType: 'json',
success (res) {
console.log('需求1: ', res)
ajax({
url: './server/b.php',
data: res,
dataType: 'json',
success (res) {
console.log('需求2: ', res)
ajax({
url: './server/c.php',
data: res,
dataType: 'json',
success (res) {
console.log('需求3: ', res)
}
})
}
})
}
})
// 多出现再面试题
// (function (cb) {
// console.log('b')
// cb(function (cb) {
// console.log('c')
// cb(new Date())
// })
// })(function (cb) {
// console.log('a')
// cb(function (res) {
// console.log(res)
// })
// })
13.Promise
/*
Promise - 承诺
+ 一个承诺多少个状态
=> 持续 pending
=> 成功 resolved
=> 失败 rejected
+ ES6 的语法
=> 专门用来解决回调地狱问题
Promise 的语法
+ Promise 是 ES6 内置的构造函数
+ 语法: new Promise(function () { 你要执行的异步的事情 })
=> 实例化的时候, 这个函数接收两个参数
=> resolve, reject
+ 语法:
=> 实例化对象身上由两个方法
1. then()
-> promise对象.then(function () {})
2. catch()
-> promise对象.catch(function () {})
=> then 方法的函数传递给了 实例化的 resolve
=> catch 方法的函数传递给了 实例化的 reject
Promise 的进阶语法
+ 当你再一个 promise 对象的 then 里面返回一个新的 promise 对象
+ 你可以再这个 then 的后面继续来一个 then 接收第一个 then 里面 promise 对象的结果
改变封装异步代码的思路
+ 按照 promise 的思想来封装异步代码
+ 分析: 为什么可以使用 then
=> 得是一个 promise 对象
+ 封装:
=> function pAjax() {}
=> 只要 pAjax 里面返回的是一个 promise 对象
=> pAjax({ 配置项 }).then()
Promise 的另一种语法
+ Promise.all()
+ 目的是把多个 promise 对象封装成一个
+ 语法: Promise.all([ promise对象1, promise对象2, ... ]).then(function () {})
+ then 里面会接收所有 promise 完成以后的结果, 以一个数组的形式给你返回
+ 致命缺点: 必须三个全部成功, 由任何一个失败, 那么最终你一个结果也得不到
*/
// const p1 = new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve('结果 1')
// }, Math.random() * 5 * 1000)
// })
// const p2 = new Promise((resolve, reject) => {
// setTimeout(() => {
// // resolve('结果 2')
// reject('结果 2 失败')
// }, Math.random() * 5 * 1000)
// })
// const p3 = new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve('结果 3')
// }, Math.random() * 5 * 1000)
// })
// 使用 all() 把他们合成一个
// Promise
// .all([ p1, p2, p3 ])
// .then(res => {
// console.log(res)
// })
// .catch(err => {
// console.log(err)
// })
// pAjax({
// url: './server/a.php',
// dataType: 'json'
// })
// .then(res => {
// console.log('需求1: ', res)
// return pAjax({
// url: './server/b.php',
// data: res,
// dataType: 'json'
// })
// })
// .then(res => {
// console.log('需求2: ', res)
// return pAjax({
// url: './server/c.php',
// data: res,
// dataType: 'json'
// })
// })
// .then(res => {
// console.log('需求3: ', res)
// })
// 2. Promise 进阶语法
// new Promise(function (resolve, reject) {
// ajax({
// url: './server/a.php',
// dataType: 'json',
// success (res) {
// resolve(res)
// }
// })
// })
// .then(function (res) {
// console.log('需求1: ', res)
// return new Promise(function (resolve, reject) {
// ajax({
// url: './server/b.php',
// data: res,
// dataType: 'json',
// success (res) {
// resolve(res)
// }
// })
// })
// })
// .then(function (res) {
// console.log('需求2: ', res)
// return new Promise(function (resolve, reject) {
// ajax({
// url: './server/c.php',
// data: res,
// dataType: 'json',
// success (res) {
// resolve(res)
// }
// })
// })
// })
// .then(function (res) {
// console.log('需求3: ', res)
// })
// then 传递的这个 function a返回什么, then 的返回值就是什么
// a 函数的返回值, 就是 then 的返回值
// const p2 = p.then(function a(res) {
// console.log('需求1: ', res)
// // 这里就是一个 promise 的 then, 再这里返回一个新的 promise 对象
// const result = new Promise(function (resolve, reject) {
// ajax({
// url: './server/b.php',
// data: res,
// dataType: 'json',
// success (res) {
// resolve(res)
// }
// })
// })
// // result 是一个新的 promise 实例化对象
// // return 以后, p2 得到的就是这个 result 新的 promise 实例化对象
// return result
// })
// p2.then(function (res) {
// console.log('需求2: ', res)
// })
// 1. promise 基础语法
// const p = new Promise(function (resolve, reject) {
// // resolve 是成功的回调函数
// // 当你书写 resolve() 的时候, 实际上是在调用 then 里面的函数
// // reject 是失败的回调函数
// // 当你书写 reject() 的时候, 实际上是在调用 catch 里面的函数
// ajax({
// url: './server/a1.php',
// dataType: 'json',
// success (res) {
// resolve(res)
// },
// error (err) {
// reject(err)
// }
// })
// })
// 调用 then 方法
// 调用 catch 方法
// p.then(function (res) {
// console.log('我执行了', res)
// })
// p.catch(function (err) {
// console.log('我失败了', err)
// })
14. async / await
/*
async / await
+ ES7 的语法
=> ES6 提出的方案, 但是 ES6 实现的不是很好
=> 再 ES7 的时候优化过
+ 目的:
1. 回调地狱的终极解决办法
2. 把异步代码写的看起来像同步代码
语法:
1. async 书写再函数的前面, 是对这个函数的修饰关键字
2. await 的使用, 必须有 async 关键字, await 才可以再函数内部使用
3. await 等待的必须是一个 promise 对象, 才会有等待的结果, 不然没有意义
+ 当你满足了以上三个条件以后, 你的 promise 对象本该再 then 里面接收的结果
=> 就可以直接定义变量接收
=> 你的 promise 里面的异步代码没有结束之前
=> 不会继续向下执行
*/
// fn 是一个异步函数
// async function fn() {
// // await 关键字, 这个函数必须要有 async
// const res = await pAjax({ url: './server/a.php', dataType: 'json' })
// // 当 pAjax 发送的请求没有回来之前, res 不会被赋值
// // 只有请求回来以后, res 才会被赋值
// // 如果这段打印先于请求回来执行, res 没有结果
// // 如果 res 有结果, 证明: 这段代码被延后执行了, 延后到后面的 promise 对象完成以后
// console.log(res)
// console.log('后续代码')
// }
// fn()
// async function fn() {
// const res1 = await pAjax({ url: './server/a.php', dataType: 'json' })
// console.log('需求1: ', res1)
// const res2 = await pAjax({ url: './server/b.php', dataType: 'json', data: res1 })
// console.log('需求2: ', res2)
// const res3 = await pAjax({ url: './server/c.php', dataType: 'json', data: res2 })
// console.log('需求3: ', res3)
// }
// console.log('start')
// fn()
// console.log('end')
const div = document.querySelector('div')
div.addEventListener('click', async () => {
const res1 = await pAjax({ url: './server/a.php', dataType: 'json' })
console.log('需求1: ', res1)
const res2 = await pAjax({ url: './server/b.php', dataType: 'json', data: res1 })
console.log('需求2: ', res2)
const res3 = await pAjax({ url: './server/c.php', dataType: 'json', data: res2 })
console.log('需求3: ', res3)
})
15. generator 函数
/*
generator 函数
+ 一种长得很像函数的玩意
+ 但是不是函数, 函数生成器(迭代器)
语法:
+ 再定义函数的时候, 再 function 后面 或者 函数名前面加一个 星号(*)
+ 函数内部可以使用一个 yield 关键字
=> 类似于 return 一样的作用
=> 可以给你制造一个结果
=> 让这个 generator 暂停
=> 当你再次回到这个 generator 的时候, 从上次 yield 继续向后执行代码
+ generator 的返回值是一个迭代器
=> 包含一个 next() 方法
=> 每一次 next 执行, 就会执行到下一个 yield 位置为止
*/
// 当有了星号以后, fn 不再是一个函数了
function* fn() {
console.log('我是第一段 代码')
yield '第一段结束'
console.log('我是第二段 代码')
yield '第二段结束'
console.log('我是第三段 代码')
return '第三段结束'
}
// result 就是 fn 给生成一个 迭代器
const result = fn()
// 第一次, 从 fn 的开头执行到第一个 yield,
// 把 yield 后面的东西当作返回值
const first = result.next()
console.log(first)
// 第二次, 从第一次的 yield 后面开始执行到第二个 yield 结束
// 把 第二个 yield 后面的东西当作返回值
const second = result.next()
console.log(second)
const third = result.next()
console.log(third)
|