一、同源策略

什么是同源策略?

同源策略(Same-origin Policy):为了保证浏览器的信息安全,浏览器采用同源策略,保证当前源中的资源只能在当前的源中使用;其他源如果需要使用当前源资源,需要特殊技术,这种A源访问B源的资源的通信称为跨域;

示例:

  1. let xhr = new XMLHttpRequest();
  2. xhr.open('GET', 'https://www.baidu.com/', true);
  3. xhr.onreadystatechange = function () {
  4. if (xhr.readyState === 4 && xhr.status === 200) {
  5. console.log('xxx')
  6. }
  7. };
  8. xhr.send();
  • 以上请求会报错:
  1. 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.

当出现以上错误时说明你正在进行一个跨域的操作;

同源策略的要求:

同源策略要求通信的两个源的协议、域名、端口号要相同,如果三者中任意一个不同就是不满足同源策略;不满足同源策略的通信就是跨域;

常用的跨域解决方案:

  1. JSONP
  2. 服务端转发,因为同源策略只在客户端存在,在服务端是不存在的;所以可以由服务端转发请求;
  3. nginx 转发,nginx 是服务器应用程序,它可以接受客户端的请求,然后根据规则可以配置自动转发;
  4. CORS: Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;

二、JSONP

JSONP 是一种常用的解决跨域的方式;

原理:

利用 script 的 src 属性是不受同源策略约束的,可以访问不同服务器或者端口号下的数据

  1. 提前声明一个叫做 fn 的函数,给 fn 设置一个形参;
  2. 在页面给 script 的 src 的指向的路径拼接一个 callback 属性,callback=fn;当浏览器解析到这个 script 标签时,会向 src 指向的路径发起 http 请求;
  3. 服务器收到这个请求后,会返回一个 fn (这里面是服务器返回的数据)
  4. fn({xxx}) 这个是让 fn 执行,小括号里面就是服务器发送给我们的数据

示例:

JS代码:

  1. function fn(data) {
  2. console.log(data);
  3. }

HTML代码

  1. <script src="http://matchweb.sports.qq.com/kbs/calendar?columnId=100000&callback=fn"></script>

三、Promise

Promise 是什么?

Promise: 是浏览器新增的内置类,用来管理异步;Promise 本身是同步的,是用来管理异步的;传递给 Promise 的构造函数的回调函数是同步执行的;

  1. let p = new Promise(function (resolve, reject) {
  2. // 这个函数中处理异步
  3. });

Promise实例对象有三种状态;

  • pending: 已经初始化,正在处理异步
  • fulfilled: 异步处理成功
  • rejected: 异步处理失败

值得注意的是,Promise 的状态一旦发生变更,就会凝固,不会在发生变化;

Promise 如何处理异步?

  1. let p2 = new Promise(function (resolve, reject) {
  2. // 这个函数是同步执行的
  3. setTimeout(function () {
  4. resolve('abc') // resolve 的实参会传递给 then 方法的第一个回调函数;
  5. })
  6. });
  • 在创建Promise示例时传递的回调函数中,存放的是异步执行的任务;
  • resolve 当异步处理成功后执行的,执行的是一个事件池,收集了后面所有的 then 方法的第一个参数;
  • reject 当异步处理失败后执行的,执行的也是一个事件池,收集了后面所有的 then 方法的第二个参数;
  1. p2.then(function (data) {
  2. console.log(1);
  3. console.log(data);
  4. // throw 'new err';
  5. // return 'xyz'
  6. return new Promise(function (resolve, reject) {
  7. resolve('就是想解决')
  8. })
  9. }, function (err) {
  10. console.log(2);
  11. console.log(err)
  12. }).then(function (data2) {
  13. console.log(3);
  14. console.log(data2)
  15. // then方法
  16. }, function (err2) {
  17. console.log(4);
  18. console.log(err2)
  19. });
  • 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

  1. $.ajax({
  2. url: 'aside.json',
  3. type: 'get',
  4. cache: false,
  5. error (err) {},
  6. success (res) {
  7. let [ , ,third] = res;
  8. $.ajax({
  9. url: 'banner.json',
  10. type: 'get',
  11. cache: false,
  12. error (err) {},
  13. success (data) {
  14. console.log(data);
  15. }
  16. })
  17. }
  18. });

我们发现,上面代码中一层嵌套一层,如果接口多了,那么这种代码组织起来将会十分不便;

使用Promise

  1. let p = new Promise(function (resolve, reject) {
  2. $.ajax({
  3. url: 'aside.json',
  4. type: 'GET',
  5. cache: false,
  6. error(err) {
  7. reject(err)
  8. },
  9. success (data) {
  10. resolve(data)
  11. }
  12. })
  13. });
  14. p.then(([,, third]) => {
  15. return new Promise(function (resolve, reject) {
  16. $.ajax({
  17. url: 'banner.json',
  18. type: 'get',
  19. cache: false,
  20. error(err) {
  21. reject(err)
  22. },
  23. success (data) {
  24. resolve(data)
  25. }
  26. })
  27. })
  28. }).then((banner) => {
  29. console.log(banner)
  30. }).catch((err) => {
  31. console.log(err)
  32. });
  • 一般情况下,then 只放一个成功的回调函数,会把失败的回调函数中放在 catch 函数中;

五、Promise.all() 方法

  • 现在有两个接口,要求必须等着两个接口全部请求完成后才能渲染数据;

类似于上面这种场景就会用到 Promise 的 all 方法;

  1. Promise.all([promise实例1, promise实例2])
  • Promise.all 是 Promise 的静态方法,接受一个有多个 promise 实例组成的数组,并且返回一个新的 Promise 实例;
  • 如果数组中所有的 Promise 实例的状态都变成 resolve,那么返回的新的 Promise 实例的状态才能变成 resolve;
  • 如果有一个失败,那么新返回的 Promise 实例的状态就会变为失败;
  • 同时,会把数组中的 promise 实例成功的数据组成一个新的数组,传递给后面 then 方法的第一个函数;

示例:

  1. function queryFn(url) {
  2. return new Promise((resolve, reject) => {
  3. $.ajax({
  4. url: url,
  5. cache: false,
  6. error(err) {
  7. reject(err)
  8. },
  9. success (data) {
  10. resolve(data)
  11. }
  12. })
  13. })
  14. }
  15. Promise.all([queryFn('aside.json'), queryFn('banner.json')]).then((dataArr) => {
  16. console.log(dataArr)
  17. }).catch((err) => {
  18. console.log(err)
  19. });