一、什么是 promise
ES6 语法规范中新加的内置类,用来处理 js 中异步编程的,而我们所谓的 Promise 设计模式,就是基于 promise 对异步操作进行管理
promose 是一个内置类,所以创建一个 promise:new Promise([executor]): 第一个执行函数必须传递,这里的 executor 是一个回调函数下面简称 exe
new promise 的时候就会把 exe 执行,创建 promise 的一个实例(exe 是 promise 类的一个回调函数,promise 内部会把它执行)
promise 不仅把 exe 执行,而且还给 exe 传递两个参数(两个参数也是函数类型)
resolve 函数:它执行代表 promise 处理的异步事情是成功的,把 promise 的状态改为 fulfilled
reject 函数:它执行代表 promise 处理的异步事情是失败的,把 promise 的状态改为 rejectedexe 函数中放的就是当前要处理的异步操作事情
let promiseExamp = new Promise((resolve, reject) => {这里一般存放的都是我们即将要处理的异步任务,任务成功我们执行 resolve,任务失败我们执行 reject(当然写同步的也可以)}
promise执行具体细节
举个例子:
let ran = Math.random();
setTimeout(() => {
if (ran < 0.5) {
reject(ran);
return;
}
resolve(ran);
}, 1000);
promiseExamp.then(result => {
// 状态为 FULFILLED 成功后执行(RESULT:[[PromiseValue]])
console.log('成功: ' + result);
}, error => {
// 状态为REJECTED 失败后执行
console.log('失败: ' + error);
});
二、异步编程中的回调地狱
一个回调函数嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数再嵌一个回调函数… 就是回调地狱。
一个小例子:
从服务器获取某个学生的基本信息 (score-id) -> 基于分数的 ID 获取到当前学生各项成绩 -> 基于某一项成绩获取他在学校的排名
1. AJAX 的串行
只有第一个请求成功才能执行第二个,第二个成功才能执行第三个,最后一个请求成功后拿到了每一次请求的所有数据。
$.ajax({
url:'/baseInfo',
method:'GET',
data:{
name:'zhanglu'
},
success:result => {
let scoreId = result.scoreId;
$.ajax({
url:'/scoreInfo',
method:'GET',
data:{
id:scoreId
},
success:result => {
let chinese = result.chinese;
$.ajax({
url:'/paiming',
method:'GET',
data:{
num:chinese
},
success:result => {
}
});
}
});
}
});
2. AJAX 的并行
三个请求可以同时发送,但是需要等到所有请求都成功才会做一件事
let chi = 100,
eng = 12,
math = 98;
let chiPai,
engPai,
mathPai;
let count = 0;
function func () {
if(count >= 3) {
// 处理自己要做的事情
}
}
$.ajax({
url:'/pai ? chi = ' + chi,
success:result => {
chiPai = result;
count++;
func();
}
});
$.ajax({
url:'/pai ? eng = ' + eng,
success:result => {
engPai = result;
count++;
func();
}
});
$.ajax({
url:'/pai ? math = ' + math,
success:result => {
mathPai = result;
count++;
func();
}
});
3. 解决AJAX的回调地狱
function queryBase () {
return new Promise (resolve => {
$.ajax({
url: '/baseInfo?name=zhanglu',
success: result => {
resolve(result);
}
});
});
}
function queryScore (scoreId) {
return new Promise(resolve => {
$.ajax({
url: '/score?id=' + scoreId,
success: result => {
resolve(result);
}
});
});
}
function queryChinese (chinese) {
return new Promise (resolve => {
$.ajax({
url: '/paiming?chin=' + chinese,
success: result => {
resolve(result);
}
});
});
}
4. 解决AJAX的并行
function ajax1 () {
return new Promise (resolve => {
$.ajax({
url: '/api1',
success: resolve
});
});
}
function ajax2 () {
return new Promise (resolve => {
$.ajax({
url: '/api2',
success: resolve
});
});
}
function ajax3 () {
return new Promise (resolve => {
$.ajax({
url: '/api3',
success: resolve
});
});
}
三、then
1. 什么是then
Promise.prototype 上面有 then 的方法
- then: 设置成功或者失败后执行的方法(成功或者失败都可以设置,也可以只设置一个)
pro.then ([success],[error])
pro.then ([success],null)
pro.then (null,[error])
catch: 设置失败后执行的方法
finally: 设置不论成功还是失败都会执行的方法(一般不用)
new Promise((resolve, reject) => {
// resolve(100); // 把第一个 promise 实例的 value 值改为100 / -100
reject(-100);
}).then(result => {
console.log(result);
return result * 10; // then 中 return 的结果相当于把当前这个新的 promise 实例中的 value 值改为返回值
}, err => {
console.log(err);
return err / 10;
}).then(A => {
console.log('A:' + A);
}, B => {
console.log('B:' + B);
}).then(C => {
}, D => {
});
2. then 链
执行 then / catch / finally 返回的结果是一个全新的 promise 实例,所以可以链式写下去;下一个 then 中哪个方式会被执行,由上一个 then 中某个方法执行的结果来决定
上一个 then 中某个方法的返回值会传递给下一个 then 的某个方法中
如果当前 promise 实例的状态确定后,都会到对应的 then 中找方法,如果 then 中没有对应的这个方法,则会向下顺延
then 方法中如果返回的是一个 promise 实例,则当前返回实例的成功或者失败状态,影响着下一个 then 中哪个方法会被触发执行;如果返回的是非 promise 实例,则看当前方法执行是否报错,来决定下一个 then 中哪个方法执行;
new Promise((resolve, reject) => {
resolve();
}).then().catch(x => {
console.log(1);
}).then(x => {
console.log(2); // OK
}).then(x => {
console.log(3); // OK
}).catch(x => {
console.log(4);
}).then(x => {
console.log('AAA'); // OK
console.log(AAA); // 报错
}).catch().then(null, x => {
console.log(5); // OK
});
3. promise.all
Promise.all([promise1, promise2,…]):all 中存放的是多个 promise 实例(每一个实例管理者一个异步操作),执行 all 方法返回的结果是一个新的 promise 实例 “PROA”
当所有 promise 实例的状态都为 Fulfilled 的时候(成功),让 PROA 的状态也变为 Fulfilled,并且把所有 promise 成功获取的结果,存储为成为一个数组(顺序和最开始编写的顺序一致)“result=[result1,result2,…]”,让 PROA 这个数组的 value 值等于这个数组
都成功(PROA 状态是 fulfilled)才会通知 then 中第一个方法执行,只要有一个失败(PROA 状态是 recected),就会通知 then 中第二个方法或者 catch 中的方法执行
Promise.all([ajax1(), ajax3(), ajax2()]).then(results => {
// results:[result1, result3, result2]
});
Promise.race([ajax1(), ajax3(), ajax2()]).then(result => {
// 看哪一个 promise 状态最先处理完(成功或者失败),以最先处理完的为主
});
四、axios
axios:一款基于 promise 设计模式封装的 AJAX 库(JQ 中的 AJAX 就是最普通的 AJAX 库,没有基于 promise 管理)
axios.post([URL], [DATA], [OPTIONS]):DATA 通过请求主传递给服务器的内容
1. options
options 的参数
baseURL:基础的URL路径
transformRequest:处理请求参数(对POST系列有作用)
transformResponse:把返回的结果进行处理
params:GET 系列请求传递给服务器的内容(会把 PARAMS 中的内容拼接为 X-WWW-FORM-URLENCODED 这种格式,基于 URL 问号传参传递给服务器)
paramsSerializer:传递参数的序列化
timeout:超时时间
withCredentials:跨域请求中是否允许携带凭证
responseType:预设服务器返回结果的格式,默认是 JSON,支持 BUFFER / TEXT / STREAM / DOCUMENT…
validateStatus:AXIOS 本身只有在 HTTP 状态码以2开头的时候才认为是成功,其余都认为是失败状态,当然我们可以自己来设置,基于 validateStatus 这个来修改
// 执行 axios.xxx() 都会返回一个 promise 实例,AJAX 请求成功会把实例的状态改为 FULFILLED,请求失败状态改为 REJECTED;并且获取的结果或者错误原因作为 promise 的 value
axios.get('http://127.0.0.1:5500/json/data2.json', {
headers: {
AAA: encodeURIComponent('珠峰哈哈哈')
},
params: {
lx: 1,
from: 'WX'
}
}).then(result => {
// result:从服务器获取的结果
return result.data;
}).catch(reason => {
console.log(reason);
throw new Error(reason);
}).then(data => {
// data:从服务器获取的响应主体内容
// CONFIG:我们自己配置的选项信息
// DATA:存储的是响应主体内
// HEADERS:存储响应头的信息
// REQUEST:AJAX实例
// STATUS:响应状态码
// STATUS-TEXT:状态码的描述
console.log(data);
});
2. 如何使用axios
在使用 AXIOS 之前,我们一般都需要配置默认的配置项
- 基础 URL,后期再发送请求的时候,URL 请求地址最前面的公共部分就不需要再写了
axios.defaults.baseURL = "http://127.0.0.1:5500";
- 跨域请求中允许携带资源凭证(例如 COOKIE 信息)
axios.defaults.withCredentials = true;
- 设置请求头:POST 系列中,我们传递给服务器数据的格式一般以 x-www-form-urlencoded 格式为主
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
- 设置请求拦截器(只对 POST 系列有用):把基于请求主体传递给服务器的内容进行拦截,把内容格式变为 x-www-form-urlencoded 这种格式,再传递给服务器
axios.defaults.transformRequest = function (data) {
if (!data) return data;
let str = ``;
for (let key in data) {
if (!data.hasOwnProperty(key)) break;
str += `&${key}=${data[key]}`;
}
return str.substring(1);
};
- 设置响应拦截器:[成功状态]把从服务器获取的结果中的响应主体信息获取到即可,[失败状态]手动把错误信息抛出异常
axios.interceptors.response.use(function (response) {
return response.data;
}, function (error) {
throw new Error(error);
});
- 配置什么才算成功(把 PROMISE 状态改为 FULFILLED)
axios.defaults.axios.defaults.validateStatus = function (status) {
return /^(2|3)\d{2}$/.test(status);
}headers['Content-Type'] = 'application/x-www-form-urlencoded';
// Promise.all
let promise1 = Promise.resolve(100);
let promise2 = Promise.resolve(200);
axios.all([promise1, promise2]).then(results => {
let [val1, val2] = results;
console.log(val1, val2);
});
/*
axios.all([promise1, promise2]).then(axios.spread(function (val1, val2) {
// axios.spread:把基于 axios.all 获取的结果一项项的单独获取到
console.log(val1, val2);
*/
五、跨域
1. 什么是同源策略?
同源策略(Same-origin Policy):为了保证浏览器的信息安全,浏览器采用同源策略,保证当前源中的资源只能在当前的源中使用;其他源如果需要使用当前源资源,需要特殊技术,这种 A 源访问 B 源的资源的通信称为跨域;
示例:
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com/', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log('xxx')
}
};
xhr.send();
- 以上请求会报错:
Access to XMLHttpRequest at 'https://www.baidu.com/' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
当出现以上错误时说明你正在进行一个跨域的操作;
2. 同源策略的要求:
同源策略要求通信的两个源的协议、域名、端口号要相同,如果三者中任意一个不同就是不满足同源策略;不满足同源策略的通信就是跨域;
3. 常用的跨域解决方案:
- JSONP
- 服务端转发,因为同源策略只在客户端存在,在服务端是不存在的;所以可以由服务端转发请求;
- nginx 转发,nginx 是服务器应用程序,它可以接受客户端的请求,然后根据规则可以配置自动转发;
- CORS: Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;
4. JSONP
JSONP 是一种常用的解决跨域的方式;
原理:利用 script 的 src 属性是不受同源策略约束的,可以访问不同服务器或者端口号下的数据
- 提前声明一个叫做 fn 的函数,给 fn 设置一个形参;
- 在页面给 script 的 src 的指向的路径拼接一个 callback 属性,callback=fn;当浏览器解析到这个 script 标签时,会向 src 指向的路径发起 http 请求;
- 服务器收到这个请求后,会返回一个 fn (这里面是服务器返回的数据)
- fn({xxx}) 这个是让 fn 执行,小括号里面就是服务器发送给我们的数据
JS代码:
function fn(data) {
console.log(data);
}
HTML代码
<script src="http://matchweb.sports.qq.com/kbs/calendar?columnId=100000&callback=fn"></script>