day18

107 阅读11分钟

1.回调地狱

回调地狱
    + 回调函数内书写回调函数
    + 代码的阅读性和可维护性极低

  分析:
    + 因为你的封装是按照 回调函数 的模式进行封装了
    + 调用的时候, 就必须按照 回调函数 的模式进行调用
    + 解决: 从根本上解决问题, 不在按照 回调函数 的模式进行封装了
    + ES6 提供了一个新的 异步封装 方案, 叫做 Promise

  思考:
    + 今后封装 异步操作 的时候
      => 回调函数 形式
      => Promise 形式
    + 如果你是按照 回调函数 形式封装
      => 必须按照 回调函数 形式调用
      => 就会出现回调地狱
    + 如果你是按照 Promise 形式封装
      => 必须按照 Promise 的语法调用

  需求:
    1. 发送请求, 请求 /test/first 地址
    2. 发送请求, 请求 /test/second 地址, 要求, 在第一个请求结束以后在请求
    3. 发送请求, 请求 /test/third 地址, 要求, 在第二个请求结束以后在请求
    
例:
ajax({
  url: 'http://localhost:8888/test/first',
  success (res) {
    console.log('第一次的请求结果 : ', res)
    // 这个位置的代码执行, 一定是第一个成功了
    // 应该在这里发送第二个请求
    ajax({
      url: 'http://localhost:8888/test/second',
      dataType: 'json',
      success (res) {
        console.log('第一次的请求结果 : ', res)
        ajax({
          url: 'http://localhost:8888/test/third',
          data: { name: 'Jack', age: 18 },
          dataType: 'json',
          success (res) {
            console.log('第一次的请求结果 : ', res)
          }
        })
      }
    })
  }
})

2.Promise语法

Promise 语法
    + Promise 承诺
    + 一个 Promise 只有三个状态
      => peading     持续
      => fulfilled   成功
      => rejected    失败
    + 状态转换只有两种
      => 持续 -> 成功
      => 持续 -> 失败

  基础语法:
    + const p = new Promise(function () {
        // 书写你的异步代码
        // 根据代码逻辑让本次 Promise 转换状态到成功或者失败
      })
      => 私人: 对你做出一个承诺
    + p.then(function () {
        // 当前这个 Promise 状态有 持续转换到成功 的时候会执行
      })
    + p.catch(function () {
        // 当前这个 Promise 状态由 持续转换到失败 的时候会执行
      })
 
例:
    //Promise基础应用             //  a     b
const p = new Promise(function (resolve, reject) {
// 第一个参数: 是一个函数数据类型, 当调用的时候, 会被本次 Promise 状态由持续转换为成功
// 第二个参数: 是一个函数数据类型, 当调用的时候, 会把本次 Promise 状态由持续转换为失败

// 书写一段异步代码
const n = Math.floor(Math.random() * 3001 + 2000)

setTimeout(() => {
    if (n >= 3500) {
      // 表示成功
      resolve(n)                   //resolve调用下面的 p.then这个函数
    } else {
      // 表示失败
      reject('我错了')             //reject调用下面的 p.catch这个函数
    }
  }, n)

})

1.注册一个成功的回调
p.then(function (n) {
  console.log('成功了', n)
})

2.注册一个失败的回调
p.catch(function (err) {
  console.log('失败了', err)
})

3.可以直接链式编程书写(基础写法的改进)     //给个承诺 成功走他 失败走他 类似三元表达式
new Promise(function (resolve, reject) {      //整体是个承诺
  const n = Math.floor(Math.random() * 3001 + 2000)
  setTimeout(() => {
    if (n >= 3500) {
      resolve(n)
    } else {
      reject('我错了')
    }
  }, n)
})

.then(function (n) {
  console.log('成功了', n)
})

.catch(function (err) {
  console.log('失败了', err)
})

4.封装
4.1 只要我能拿到一个 Promise 对象, 就可以注册 .then 和 .catch
function fn() {
  const p = new Promise(function (resolve, reject) {
  const n = Math.floor(Math.random() * 3001 + 2000)
    setTimeout(() => {
      if (n >= 3500) {
        resolve(n)          
      } else {
        reject('我错了')    
      }
    }, n)
  })

  // 把这个 Promsie 对象当做返回值
  return p
}

让这个承诺开始执行
res 接受的是, fn 函数内的返回值, 也就是 fn 函数内的 p
实际上就是一个 Promsie 对象
//1.写法一
const res = fn()
res.then(function (n) {
  console.log('成功了', n)
})
res.catch(function (err) {
  console.log('失败了', err)
})
//2.写法二(用这个)
fn()
  .then(function (n) {
    console.log('成功了', n)
  })
  .catch(function (err) {
    console.log('失败了', err)
  })
  
  
4.2 尝试 ajax 的二次封装
pAjax({ url: 'http://localhost:8888/test/first', dataType: 'json' })
.then(res => console.log('请求成功 : ', res))
.catch(err => console.log('请求失败 : ', err))


4.3 尝试三个需求
/*
  需求:
    1. 发送请求, 请求 /test/first 地址
    2. 发送请求, 请求 /test/second 地址, 要求, 在第一个请求结束以后在请求
    3. 发送请求, 请求 /test/third 地址, 要求, 在第二个请求结束以后在请求

  Promise 有一个特殊的调用方式  !!!!!!!链式
    + 可以链式 then 书写
    + 当你前一个 then 里面 return 一个 Promise 对象的时候
    + 可以在前一个 then 后面再写一个 then
*/

pAjax({ url: 'http://localhost:8888/test/first' })
  .then(res => {
    console.log('第一个请求成功 : ', res)
    return pAjax({ url: 'http://localhost:8888/test/second', dataType: 'json' })
  })
  .then(res => {
    console.log('第二个请求成功 : ', res)
    return pAjax({ url: 'http://localhost:8888/test/third', data: { name: 'Jack', age: 18 }, dataType: 'json' })
  })
  .then(res => {
    console.log('第三个请求成功 : ', res)
  })

3.async 和 await 关键字

asyncawait 关键字
    + 目的: 为了把异步代码写的看起来像同步代码(不会修改异步的本质)

async 关键字
    + 书写在函数的前面
    + 目的:
      => 为了可以再该函数内使用 await 关键字

await 关键字
    + 书写在一个有 async 关键字的函数内
    + await 等待的意思
      => await 后面得到的必须是一个 Promise 对象
    + 目的:
      => 你本该在 then 内接受的结果, 可以直接定义变量接受了

例:
fn()
async function fn() {
  // await 关键字会让当前函数内的代码等待, 等到后面这个 Promise 完全完成
  // 把结果给到 r1 变量以后, 在继续向后执行代码
  const r1 = await pAjax({ url: 'http://localhost:8888/test/first' })
  console.log(r1)

  // 这里的代码能执行, 说明前一个 Promise 必须完成
  const r2 = await pAjax({ url: 'http://localhost:8888/test/second', dataType: 'json' })
  console.log(r2)

  // 这里的代码能执行, 说明前一个 Promise 必须完成
  const r3 = await pAjax({ url: 'http://localhost:8888/test/third', data: { name: 'Jack', age: 18 }, dataType: 'json' })
  console.log(r3)
}

进化:

微信图片_20220623190823.png

4.宏任务和微任务

1. js 是事件驱动型语言
任何的操作都是事件驱动处理
把每个操作可以理解为一个任务
而任务中分为同步和异步任务
同步任务是指当前任务执行完成后再执行下一个任务 ,会等待当前任务执行完成后,在执行后续
异步任务会将当前任务存放在一个特殊任务列表中,当时间到了,或者事情完成了,调用
这个任务列表中设定的任务执行
    
2.这些都是异步的(除了下面这些 其他都是同步)
加载图片、文件
与服务器通信
setTimeout setInterval setImmediate(暂时未学)   宏任务
Promise process.nextTick()(暂时未学)            微任务  async函数返回的结果也是Promise

3.同步任务、宏任务、微任务执行顺序(记)
个人语义化: 同步任务先执行 微任务后执行 宏任务最后执行 
但是宏任务分层级 宏任务第一层先执行 层层递进 

如图:

微信图片_20220623212813.png

例:
例子1.-任务顺序
    setTimeout(function(){       //宏任务等所有任务需求都完成才执行宏任务
        console.log("c"); //3
    },1000);
    //如果这个时间比同步任务的时间要更快完成,就会等待同步任务全部执行完成后立即执行这个异步任务
    var time=Date.now();
    console.log("a");  //1
    for(var i=0;i<1000000000;i++){

    }
    console.log("b"); //2
    console.log(Date.now()-time); //a b  2580 c 


例子2.
    console.log(1);  //1
    setTimeout(function(){        //没写时间一般认为是同步任务的下一个任务 
        console.log(2); //6
    });
    console.log(3); //2
    new Promise(function(resolve,reject){
        console.log(4);//3       //这是同步任务   promise里只有.them和.catch才是异步
        resolve();
    }).then(function(){
     console.log(5)//5          //Promise的then或者catch才是异步任务
    });
    console.log(6)//4

    var time=Date.now();
    setTimeout(function(){
        console.log(Date.now()-time);
        // 当没有设置时间时,下一帧执行这个函数,
    })
    
    
例子3.
    setTimeout(function(){
        Promise.resolve().then(function(){
            console.log(1);   //1 宏任务里套微任务    他快
        })
    });
    Promise.resolve().then(function(){
        setTimeout(function(){
            console.log(2);  //2 微任务里套宏任务
        })
    })
    
如图:

微信图片_20220623213501.png

5.Cookie

/* 浏览器的本地存储 - cookie

  本地存储
    + 利用浏览器帮我们存储一些信息
    + 可以
      => 跨页面通讯
      => 前后端交互
      => 免密登录 直接
    + 有多种
      => cookie
      => localStorage
      => sessionStorage


  cookie 的特点(熟读并背诵全文)    
    1. cookie 是基于域名存储的
      => 哪一个域名存储, 哪一个域名使用
      => 本地文件是不行的
    2. cookie 的存储有大小限制
      => 4KB 左右
      => 50 条左右
    3. cookie 的存储数据格式
      => 只能是字符串格式的内容
      => 格式: 'key=value; key2=value'
    4. cookie 存储是有时效性的
      => 默认是会话级别的时效, 关闭浏览器自动删除               
      => 可以手动设置过期时间, 不管是否关闭浏览器, 都会计时          (七天免登陆)
    5. 前后端传输
      => 只要 cookie 空间内有内容
      => 在当前页面内发送的所有给后端的信息, 都会自动携带 cookie
    6. 前后端操作
      => 前端可以依靠 js 来操作
      => 后端可以依靠任何语言操作
*/


/*
  服务器打开页面
    + 页面打开方式有两种
      => 本地打开
        -> file://
      => 依赖服务器打开
        -> http://
        -> https://
        -> ...
    + 如何进行服务器打开
      => 暂时下载一个 vscode 的插件 live server
      => 打开页面的时候, 你的编辑器文件目录内必须要有文件夹
      => 右键, open with live server
        -> 快捷键 alt + l 和 alt + o
      => vscode 就会在你的电脑上启动一个临时服务器, 支持该页面的打开
*/

/*
  cookie 的设置 
    + 语法: document.cookie = 'key=value'
    + 一次只能设置一条

  cookie 设置的时候加上修饰信息
    + 语法: document.cookie = 'key=value; 修饰信息'
    + 修饰一个过期时间
      => document.cookie = 'key=value;expires=时间对象'
*/

// 1.设置一条 cookie
document.cookie = 'a=100'
document.cookie = 'a=200'  //他的设置不同名 第二次设置就是修改
document.cookie = 'b=200'


// 2.设置一条带有过期时间的 cookie
/*
  当你给 cookie 设置时效的时候, 不管你给他的是什么时间节点
  他都当做世界标准时间来使用
  你设置的是 15:35, cookie 就把 15:35 当做世界标准时间来设置
  本条 cookie 会在世界标准时间 15:35 分过期
  当前终端时间 23:35 分过期
*/
var time = new Date('2022-4-21 7:38:30') 
document.cookie = 'a=100;expires=' + time


/*
  3.设置一条 30s 以后过期的 cookie
    + 需要拿到一个当前时间节点的时间对象
    + 向前调整时间 8 小时
    + 再向后调整事件 30s
*/
//这就是设置一个cookie
var time = new Date()
time.setTime(time.getTime() - 1000 * 60 * 60 * 8 + 1000 * 30)

document.cookie = 'a=100;expires=' + time

4.封装一个设置 cookie 的方法
// 参数:
//   key      你设置的 cookie 的名字
//   value    你设置的 cookie 的值
//   expires  你设置的过期时间, 选填, 没写, 默认按照会话级别(浏览器关了就没了)
function setCookie(key, value, expires) {
  // 判断 expores 是否存在
  if (expires) {
    // 调整一个时间对象
    var time = new Date()
    time.setTime(time.getTime() - 1000 * 60 * 60 * 8 + 1000 * expires)
  }

  // 直接设置 cookie
  document.cookie = `${ key }=${ value }; expires=${ time }`
}

// 你将来使用的时候
// 需要设置一个 30s 以后过期的 cookie
setCookie('a', 100, 30)
setCookie('b', 200)

// 删除 cookie
function delCookie(key) {     
  setCookie(key, '', -1)
}

/*
  获取 cookie
    + 语法: document.cookie
    + 得到: 完整的 cookie 字符串
*/

function getCookie() {
  // 1. 拿到 cookie 字符串
  var cookie = document.cookie
  console.log(cookie)

  // 2. 准备一个空对象
  var obj = {}

  // 3. 按照 ';'
  var tmp = cookie.split('; ')

  // 4. 遍历 tmp
  tmp.forEach(function (item) {
    var t = item.split('=')
    obj[t[0]] = t[1]
  })

  // 5. 返回 obj
  return obj
}

var res = getCookie()
console.log(res)

5.1 Cookie补充

1.cookie在向服务器发起请求时,会自动携带发送到服务器,而服务器在
处理完成后也可以写入到用户的计算机内。也就是cookie会随着网络请求往返于用户端和服务端之间
因为具备服务器写入和自动携带性,所以一般用于作为自动登录的存储
用于记录用户行为和信息,以到达广告的精准投放
2、cookie存储的数据不能过大,大小仅能在5K
3、cookie存储可以按照时间达到存储时长的要求 expires=格林尼治时间字符串
4、cookie有路径关系和域名关系,跨域是不能相互访问,双击文件打开是无法使用cookie,
父级路径是无法访问子级路径中cookie,子级路径是可以访问父级
路径中cookie,可以通过设置Path=路径 存储当前cookie
5、cookie是不安全,明文存储,所以所有存储信息需要加密

6.webStorage

 /*
  面试的坑:
  解释以下 cookie localStorage sessionStorage session 的区别
*/
/*
  浏览器的本地存储 - storage
    + localStorage 和 sessionStorage
    + 浏览器本地的存储     (除了可以存储变量外,还可以存储所有文本数据)
    + 特点:
      => 只能存储字符串类型
      => 如果你需要存储的是 对象或者数组 相关的内容  
      => 转换为 json 格式存储

  storage 和 cookie 的区别
    1. 存储位置
      + cookie 以域名为基础存储(没有域名不能读写)
      + storage 以域名为基础存储(没有域名可以读写)
    2. 存储大小
      + cookie 4KB 左右
      + storage 20MB 左右
    3. 时效性
      => cookie  默认会话级别时效, 可以手动设置过期时间
      => localStorage   必然是永久存储
      => sessionStorage 必然是会话存储
    4. 前后端交互
      => cookie 会自动携带
      => storage 不会自动携带
    5. 前后端操作
      => cookie   前后端都可以操作
      => storage  只能前端操作 js

  session
    + 是一个服务端的存储空间
*/


/*
  localStorage 的操作 (也不接受重名)
    + JS 提供了完整的 增删改查 语法

  增:
    + 语法: window.localStorage.setItem('key', 'value')

  查:
    + 语法: window.localStorage.getItem('key')

  删:
    + 语法: window.localStorage.removeItem('key')

  清空:
    + 语法: window.localStorage.clear()
*/

var obj = { name: 'Jack', age: 18, gender: '男' }
var arr = [
  { name: 'Jack', age: 18, gender: '男' },
  { name: 'Rose', age: 20, gender: '女' },
  { name: 'Tom', age: 22, gender: '男' }
]

// 1. 增
window.localStorage.setItem('a', 100) //只能存储字符串类型
window.localStorage.setItem('user_info', JSON.stringify(obj))//如果你需要存储的是 对象或者数组 相关的内容
window.localStorage.setItem('user_list', JSON.stringify(arr))//转换为 json 格式存储

// 2. 查
var res = JSON.parse(window.localStorage.getItem('user_info'))
console.log(res)

// 3. 删
window.localStorage.removeItem('user_list')

// 4. 清空
window.localStorage.clear()


/*
  sessionStroage 的基本操作
    + JS 提供了完整的 增删改查 操作方法

  增:
    + 语法: window.sessionStorage.setItem('key', 'value')

  查:
    + 语法: window.sessionStorage.getItem('key')

  删:
    + 语法: window.sessionStorage.removeItem('key')

  清空:
    + 语法: window.sessionStorage.clear()
*/

var obj = { name: 'Jack', age: 18, gender: '男' }
var arr = [
  { name: 'Jack', age: 18, gender: '男' },
  { name: 'Rose', age: 20, gender: '女' },
  { name: 'Tom', age: 22, gender: '男' }
]

// 1. 增
window.sessionStorage.setItem('a', '100')
window.sessionStorage.setItem('user_info', JSON.stringify(obj))
window.sessionStorage.setItem('user_list', JSON.stringify(arr))

// 2. 查
var res = window.sessionStorage.getItem('a')
console.log(res)

// 3. 删
window.sessionStorage.removeItem('user_list')

// 4. 清空
window.sessionStorage.clear()