一、同源策略
什么是同源策略?
同源策略(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.
当出现以上错误时说明你正在进行一个跨域的操作;
同源策略的要求:
同源策略要求通信的两个源的协议、域名、端口号要相同,如果三者中任意一个不同就是不满足同源策略;不满足同源策略的通信就是跨域;
常用的跨域解决方案:
- JSONP
- 服务端转发,因为同源策略只在客户端存在,在服务端是不存在的;所以可以由服务端转发请求;
- nginx 转发,nginx 是服务器应用程序,它可以接受客户端的请求,然后根据规则可以配置自动转发;
- CORS: Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;
二、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>
三、Promise
Promise 是什么?
Promise: 是浏览器新增的内置类,用来管理异步;Promise 本身是同步的,是用来管理异步的;传递给 Promise 的构造函数的回调函数是同步执行的;
let p = new Promise(function (resolve, reject) {
// 这个函数中处理异步
});
Promise实例对象有三种状态;
- pending: 已经初始化,正在处理异步
- fulfilled: 异步处理成功
- rejected: 异步处理失败
值得注意的是,Promise 的状态一旦发生变更,就会凝固,不会在发生变化;
Promise 如何处理异步?
let p2 = new Promise(function (resolve, reject) {
// 这个函数是同步执行的
setTimeout(function () {
resolve('abc') // resolve 的实参会传递给 then 方法的第一个回调函数;
})
});
- 在创建Promise示例时传递的回调函数中,存放的是异步执行的任务;
- resolve 当异步处理成功后执行的,执行的是一个事件池,收集了后面所有的 then 方法的第一个参数;
- reject 当异步处理失败后执行的,执行的也是一个事件池,收集了后面所有的 then 方法的第二个参数;
p2.then(function (data) {
console.log(1);
console.log(data);
// throw 'new err';
// return 'xyz'
return new Promise(function (resolve, reject) {
resolve('就是想解决')
})
}, function (err) {
console.log(2);
console.log(err)
}).then(function (data2) {
console.log(3);
console.log(data2)
// then方法
}, function (err2) {
console.log(4);
console.log(err2)
});
promise 实例对象的第一个 then 方法的回调函数会根据 new Promise() 时异步处理的情况来决定
如果是 resolve 状态,就会执行第一个 then 方法的第一个回调函数,resolve 时传递的实参会传递给第一函数;
如果是 reject 就会执行第二个,reject 时传递的实参会传给第二个函数
但是后面的 then 方法里面的回调函数执行哪一个取决于执行前面的 then 方法中函数执行的情况;
如果前一个 then 中回调没有返回 promise 实例,无论是第一个还是第二个执行成功了,都会执行后面的 then 的第一个回调函数。但是前一个 then 方法中报错了,就会执行第二个;
- 如果前一个 then 方法中返回的是一个 promise 实例,后面的 then 方法中执行哪个取决于上一个 then 中返回的 promise 实例的状态,如果是 resolve 了,就会执行后面的第一个函数,如果是 reject 了,就会执行第二个函数;
四、Promise用法示例
- 首先请求 aside.json,然后从 aside.json 中取得最后一条数据的 id,然后再请求 banner.json。最后把 banner.json 输出
使用jq
$.ajax({
url: 'aside.json',
type: 'get',
cache: false,
error (err) {},
success (res) {
let [ , ,third] = res;
$.ajax({
url: 'banner.json',
type: 'get',
cache: false,
error (err) {},
success (data) {
console.log(data);
}
})
}
});
我们发现,上面代码中一层嵌套一层,如果接口多了,那么这种代码组织起来将会十分不便;
使用Promise
let p = new Promise(function (resolve, reject) {
$.ajax({
url: 'aside.json',
type: 'GET',
cache: false,
error(err) {
reject(err)
},
success (data) {
resolve(data)
}
})
});
p.then(([,, third]) => {
return new Promise(function (resolve, reject) {
$.ajax({
url: 'banner.json',
type: 'get',
cache: false,
error(err) {
reject(err)
},
success (data) {
resolve(data)
}
})
})
}).then((banner) => {
console.log(banner)
}).catch((err) => {
console.log(err)
});
- 一般情况下,then 只放一个成功的回调函数,会把失败的回调函数中放在 catch 函数中;
五、Promise.all() 方法
- 现在有两个接口,要求必须等着两个接口全部请求完成后才能渲染数据;
类似于上面这种场景就会用到 Promise 的 all 方法;
Promise.all([promise实例1, promise实例2])
- Promise.all 是 Promise 的静态方法,接受一个有多个 promise 实例组成的数组,并且返回一个新的 Promise 实例;
- 如果数组中所有的 Promise 实例的状态都变成 resolve,那么返回的新的 Promise 实例的状态才能变成 resolve;
- 如果有一个失败,那么新返回的 Promise 实例的状态就会变为失败;
- 同时,会把数组中的 promise 实例成功的数据组成一个新的数组,传递给后面 then 方法的第一个函数;
示例:
function queryFn(url) {
return new Promise((resolve, reject) => {
$.ajax({
url: url,
cache: false,
error(err) {
reject(err)
},
success (data) {
resolve(data)
}
})
})
}
Promise.all([queryFn('aside.json'), queryFn('banner.json')]).then((dataArr) => {
console.log(dataArr)
}).catch((err) => {
console.log(err)
});