封装 AJAX

  • ajax 使用起来太麻烦,因为每次都要写很多的代码
  • 那么我们就封装一个 ajax 方法来让我们使用起来简单一些

确定一下使用的方式

  • 因为有一些内容可以不传递,我们可以使用默认值,所以选择对象传递参数的方式

    1. // 使用的时候直接调用,传递一个对象就可以
    2. ajax({
    3. url: '', // 请求的地址
    4. type: '', // 请求方式
    5. async: '', // 是否异步
    6. data: '', // 携带的参数
    7. dataType: '', // 要不要执行 json.parse
    8. success: function () {} // 成功以后执行的函数
    9. })
    • 确定好使用方式以后,就开始书写封装函数

封装

  1. function ajax(options) {
  2. // 先准备一个默认值
  3. var defInfo = {
  4. url: '', // 地址不需要默认值
  5. type: 'GET', // 请求方式的默认值是 GET
  6. async: false, // 默认值是异步
  7. data: '', // 参数没有默认值
  8. dataType: 'string', // 默认不需要执行 json.parse
  9. success () {}, // 默认是一个函数
  10. }
  11. // 先来判断一下有没有传递 url,如果没有,直接抛出异常
  12. if (!options.url) {
  13. throw new Error('url 必须传递')
  14. }
  15. // 有了 url 以后就,我们就把用户传递的参数和我们的默认数据合并
  16. for (let key in options) {
  17. defInfo[key] = options[key]
  18. }
  19. // 接下来的一切我们都是使用我们的 defInfo 就可以了
  20. // 第一步就是判断参数 data
  21. // data 可以不传递,可以为空
  22. // data 也可以是一个 key=value&key=value 格式的字符串
  23. // data 也可以是一个对象
  24. // 否则就抛出异常
  25. if (!(typeof defInfo.data === 'string' && /^(\w+=\w+&?)*$/.test(defInfo.data) || Object.prototype.toString.call(defInfo.data) === '[object Object]')) {
  26. throw new Error('请按照要求传递参数')
  27. }
  28. // 参数处理完毕以后,在判断 async 的数据类型
  29. // 只能传递 布尔数据类型
  30. if (typeof defInfo.async !== 'boolean') {
  31. throw new Error('async 参数只接受布尔数据类型')
  32. }
  33. // 在接下来就判断 type
  34. // 请求方式我们只接受 GET 或着 POST
  35. if (!(defInfo.type.toUpperCase() === 'GET' || defInfo.type.toUpperCase() === 'POST')) {
  36. throw new Error('目前本插件只接受 GET 和 POST 方式,请期待更新')
  37. }
  38. // 接下来就是判断 success 的判断,必须是一个函数
  39. if (Object.prototype.toString.call(defInfo.success) !== '[object Function]') {
  40. throw new Error('success 只接受函数数据类型')
  41. }
  42. // 参数都没有问题了
  43. // 我们就要把 data 处理一下了
  44. // 因为 data 有可能是对象,当 data 是一个对象的时候,我们要把它转换成一个字符串
  45. var str = ''
  46. if (Object.prototype.toString.call(defInfo.data) === '[object Object]') {
  47. for (let attr in defInfo.data) {
  48. str += `${attr}=${defInfo.data[attr]}&`
  49. }
  50. str = str.slice(0, -1)
  51. defInfo.data = str
  52. }
  53. // 参数全部验证过了以后,我们就可以开始进行正常的 ajax 请求了
  54. // 1. 准备一个 ajax 对象
  55. // 因为要处理兼容问题,所以我们准备一个函数
  56. function createXHR() {
  57. if (XMLHttpRequest) {
  58. return new XMLHttpRequest()
  59. } else {
  60. return new ActiveXObject('Microsoft.XMLHTTP')
  61. }
  62. }
  63. // 2. 创建一个 ajax 对象
  64. var xhr = createXHR()
  65. // 3. 进行 open
  66. xhr.open(defInfo.type, defInfo.url + (defInfo.type.toUpperCase() === 'GET' ? `?${defInfo.data}&_=${new Date().getTime()}` : ''), defInfo.async)
  67. if (defInfo.type.toUpperCase() === 'POST') {
  68. xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
  69. }
  70. // 4. 进行 send
  71. xhr.send((defInfo.type.toUpperCase() === 'POST' ? `${defInfo.data}` : ''))
  72. // 5. 接受响应
  73. xhr.onreadystatechange = function () {
  74. if (xhr.readyState === 4 && /2\d{2}/.test(xhr.status)) {
  75. // 表示成功,我们就要执行 success
  76. // 但是要进行 dataType 的判断
  77. if (defInfo.dataType === 'json') {
  78. defInfo.success(JSON.parse(xhr.responseText))
  79. } else {
  80. defInfo.success()
  81. }
  82. }
  83. }
  84. }

Promise

  • promise 是一个 ES6 的语法
  • 承诺的意思,是一个专门用来解决异步 回调地狱 的问题

回调函数

  • 什么是回调函数?
  • 就是把函数 A 当作参数传递到 函数 B 中
  • 在函数 B 中以行参的方式进行调用 ```javascript function a(cb) { cb() }

function b() { console.log(‘我是函数 b’) }

a(b)

  1. - 为什么需要回调函数
  2. - 当我们执行一个异步的行为的时候,我们需要在一个异步行为执行完毕之后做一些事情
  3. - 那么,我们是没有办法提前预知这个异步行为是什么时候完成的
  4. - 我们就只能以回调函数的形式来进行
  5. - 就比如我们刚刚封装过的那个 `ajax` 函数里面的 `success`
  6. - 我们并不知道 ajax 请求什么时候完成,所以就要以回调函数的形式来进行
  7. <a name="1819c9ad"></a>
  8. ## 回调地狱
  9. - 当一个回调函数嵌套一个回调函数的时候
  10. - 就会出现一个嵌套结构
  11. - 当嵌套的多了就会出现回调地狱的情况
  12. - 比如我们发送三个 ajax 请求
  13. - 第一个正常发送
  14. - 第二个请求需要第一个请求的结果中的某一个值作为参数
  15. - 第三个请求需要第二个请求的结果中的某一个值作为参数
  16. ```javascript
  17. ajax({
  18. url: '我是第一个请求',
  19. success (res) {
  20. // 现在发送第二个请求
  21. ajax({
  22. url: '我是第二个请求',
  23. data: { a: res.a, b: res.b },
  24. success (res2) {
  25. // 进行第三个请求
  26. ajax({
  27. url: '我是第三个请求',
  28. data: { a: res2.a, b: res2.b },
  29. success (res3) {
  30. console.log(res3)
  31. }
  32. })
  33. }
  34. })
  35. }
  36. })
  • 回调地狱,其实就是回调函数嵌套过多导致的

image.png

  • 当代码成为这个结构以后,已经没有维护的可能了
  • 所以我们要把代码写的更加的艺术一些

PROMISE

  • 为了解决回调地狱
  • 我们就要使用 promise 语法
  • 语法:

    1. new Promise(function (resolve, reject) {
    2. // resolve 表示成功的回调
    3. // reject 表示失败的回调
    4. }).then(function (res) {
    5. // 成功的函数
    6. }).catch(function (err) {
    7. // 失败的函数
    8. })
  • promise 就是一个语法

    • 我们的每一个异步事件,在执行的时候
    • 都会有三个状态,执行中 / 成功 / 失败
  • 因为它包含了成功的回调函数
  • 所以我们就可以使用 promise 来解决多个 ajax 发送的问题

    1. new Promise(function (resolve, reject) {
    2. ajax({
    3. url: '第一个请求',
    4. success (res) {
    5. resolve(res)
    6. }
    7. })
    8. }).then(function (res) {
    9. // 准备发送第二个请求
    10. return new Promise(function (resolve, reject) {
    11. ajax({
    12. url: '第二个请求',
    13. data: { a: res.a, b: res.b },
    14. success (res) {
    15. resolve(res)
    16. }
    17. })
    18. })
    19. }).then(function (res) {
    20. ajax({
    21. url: '第三个请求',
    22. data: { a: res.a, b: res.b },
    23. success (res) {
    24. console.log(res)
    25. }
    26. })
    27. })
  • 这个时候,我们的代码已经改观了很多了

  • 基本已经可以维护了
  • 但是对于一个程序员来说,这个样子是不够的
  • 我们还需要更加的简化代码
  • 所以我们就需要用到一个 es7 的语法了
  • 叫做 async/await

ASYNC/AWAIT

  • async/await 是一个 es7 的语法
  • 这个语法是 回调地狱的终极解决方案
  • 语法:

    1. async function fn() {
    2. const res = await promise对象
    3. }
  • 这个是一个特殊的函数方式

  • 可以 await 一个 promise 对象
  • 可以把异步代码写的看起来像同步代码
  • 只要是一个 promiser 对象,那么我们就可以使用 async/await 来书写

    1. async function fn() {
    2. const res = new Promise(function (resolve, reject) {
    3. ajax({
    4. url: '第一个地址',
    5. success (res) {
    6. resolve(res)
    7. }
    8. })
    9. })
    10. // res 就可以得到请求的结果
    11. const res2 = new Promise(function (resolve, reject) {
    12. ajax({
    13. url: '第二个地址',
    14. data: { a: res.a, b: res.b },
    15. success (res) {
    16. resolve(res)
    17. }
    18. })
    19. })
    20. const res3 = new Promise(function (resolve, reject) {
    21. ajax({
    22. url: '第三个地址',
    23. data: { a: res2.a, b: res2.b },
    24. success (res) {
    25. resolve(res)
    26. }
    27. })
    28. })
    29. // res3 就是我们要的结果
    30. console.log(res3)
    31. }
    • 这样的异步代码写的就看起来像一个同步代码了