50行不到实现Promise化的jsonp

第一次发文章,有点小紧张。

什么是同源策略?

为什么要有跨域限制?

为什么script标签能跨域?

这些想必不用说明了,我们切入正题:

再学习黄老师的【Vue 2.0开发企业级移动端音乐Web APP】时,因为课程的数据是通过Jsonp获取的,当时听到讲解原理,不甚理解。于是点开jsonp包的源码研磨一番,原来实现原理是这样的:

1. 客户端需要在自己这边有一个定义好的接收服务端数据的函数

2. 使用script标签发起一个服务端地址的请求

3. 服务端响应,对应的函数执行,传参完成

这是最简单的jsonp实现,这里需要写一个函数,需要写一个script标签,好像一点都不高级~

接下来我们手写一个jsonp,当然了,服务端怎么知道你本地的准备函数是什么,需要客户端告诉它,而且服务端会根据客户端提供的其他参数动态生成数据给到我们。所以我们主要是实现以下几个点:

1. 调用时动态创建script标签

2. 注册一个全局的方法等待被执行

3. 等待函数一定要在标签发起请求之前准备好

4. 得到数据之后移除创建的script标签

5. 函数名不能重名,可能会同时发起多个请求

从上所述,在这里我们的方法需要以下几个参数:

. url: 后端地址

. prefix: 执行函数名的前缀,后缀使用自增来确保唯一性

. param: 与后端协商好的发起jsonp请求时的字段

. timeout: 请求超时时间

. data: 服务端需要的其他参数

jsonp实现跨域请求也有自己的局限,如只能发起get请求,原有的jsonp包url后面的参数需要自己添加上,使用的是回调函数的形式处理数据,使用的是ES5的写法,有了以上几个问题,我们可以简单的重构下:

// 其他参数在opts内
function jsonp(url, opts) {
    // 实现Promise化
    return new Promise((resolve, reject) => {
      // 自增值初始化 
      let count = 0;
      //设置默认参数
      const { 
        prefix = '__jp',
        param = 'callback',
        timeout = 60000,
        data = {}
      } = opts;
      let name = prefix + count++;
      let timer;
      //清除script标签以及注册的全局函数以及超时定时器
      function cleanup() { // 清除函数
        if (script.parentNode) {
          script.parentNode.removeChild(script);
          window[name] = null;
          if (timer) {
            clearTimeout(timer);
          }
        }
      }
      if (timeout) { // 超时
        timer = setTimeout(() => {
          cleanup();
          reject('timeout');
        }, timeout);
      }
      // 注册全局函数,等待执行中...
      window[name] = res => {
        // 只要这个函数一执行,就表示请求成功,可以使用清除函数了
        if (window[name]) {
          cleanup();
        }
        // 将请求到的数据扔给then
        resolve(res);
      }
      // 以下将data对象格式的参数拼接到url的后面
      let str = '';
      for (const key in data) {
        const value = data[key] !== undefined ? data[key] : '';
        str += `&${key}=${encodeURIComponent(value)}`;
      }
      url = url + (url.indexOf('?') > 0 ? '' : '?') + str.substr(1);
      最后加上与服务端协商的jsonp请求字段
      url = `${url}&${param}=${name}`;
      const script = document.createElement('script');
      script.src = url;
      // 以下这条执行且成功后,全局等待函数就会被执行
      document.head.appendChild(script);
    })
  }
复制代码

# 大功告成,我们请求一个QQ音乐的轮播图数据试下:

const url = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg';
  const opts = {
    data: {
      g_tk: 1928093487,
      inCharset: 'utf-8',
      outCharset: 'utf-8',
      notice: 0,
      format: 'jsonp',
      platform: 'h5',
      uin: 0,
      needNewCode: 1
    },
    // QQ音乐接口Jsonp字段
    param: 'jsonpCallback'
  }
  
  jsonp(url, opts)
    .then(res => {
      console.log(res);
    })
    .catch(ex => {
      console.log(ex);
    })
复制代码

数据请求成功!

转载于:https://juejin.im/post/5c7e89d65188255dce5dda11

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值