7-3 前后分离(异步、回调)

一、异步与回调

  • JS异步编程模型

1.什么是异步?什么是同步?

  • 如果能直接拿到结果——同步
    1.比如在医院排队挂号,你拿到号才能离开窗口
    2.同步任务可能消耗10毫秒,也可能需要3秒
    3.总之不拿到结果不会离开
  • 如果不能直接拿到结果——异步
    1.比如在餐厅门口等位置,拿到号可以去逛街,先做其他事
    2.什么时候能真正吃饭呢?
    3.你可以每10分钟取餐厅问一下(轮询)
    4.你也可以扫码用微信接收通知(回调)
  • 轮询和回调都可以拿到结果,但两者形式不同
    前者自己去问,后者是接收通知

2.异步举例

2.1 以AJAX为例

  • request.send() 之后,并不能直接得到response
  • 可以在request.send() 之后加一个 console.log(request.response),是没办法得到结果的
    1.但当添加一个延迟函数,2秒之后在console.log,就可以得到response
    2.这是因为:在JS中成功发送网络请求到得到响应大概需要几百毫秒至一两秒之间
  • 必须要等到 readyState 变成4后,浏览器回头调用 request.onreadystatechang 函数
  • 我们才能得到 request.response
  • 就像是餐厅吃饭排队,我们留一个电话号码在那里,到号就给我们打电话
    这里是:先写一个函数在那里,一旦readyState=4,浏览器就去调用函数
  request.send();
  console.log(request.response);//无法打出response
  setTimeout(() => {//两秒后可以打出response
    console.log(request.response);
  }, 2000);

2.2 回调 callback

  • 回头调用:写了一个函数却不自己调用,给别人调用,就是回调。[回头你调用以下呗~]
  • 你写给自己用的函数,不是回调
  • 你写给别人用的函数,就是回调
  • request.onreadystatechang 就是我写给浏览器调用的
  • 意思就是 你(浏览器)回头调一下这个函数
  • 在中文里,[回头] 也有 [将来] 的意思,比如 [回头请你吃饭]

2.3 回调举例

  • 代码
function f1(){}
function f2(fn){
  fn()
}
f2(f1)
  • 分析
    1.我调用 f1 没有? ——没有
    2.我把 f1 传给 f2(别人) 了没有——传了
    3.f2 调用 f1 了没有?——f2 调用了 f1
    4.那么,f1 是不是我写给 f2 调用的函数?——是
    5.所以 f1是回调,f2不是回调

3.异步和回调的关系

3.1 关联

  • 异步任务需要在得到结果时通知JS(已经走了的JS代码)来拿结果
  • 怎么通知呢?
  • 可以让 JS 留一个函数地址(电话号码)给浏览器
  • 异步任务完成时,浏览器调用该函数地址即可(拨打电话)
  • 同时把结果作为参数传给该函数(电话里说可以来吃了)
  • 这个函数使我写给浏览器调用的,所以是回调函数

3.2 区别

  • 异步任务需要用到回调函数来通知结果(但不是一定要用回调,还可以用轮询)
  • 回调函数不一定只用在异步任务里
  • 回调可以用到同步任务里
  • array.forEach(n=>console.log(n))就是同步回调
    传了一个函数给 forEach ,但没有异步的代码

4.异步函数的例子

  • 如何知道一个函数是同步还是异步?

4.1 判断同步异步

  • 如果一个函数的返回值处于
    1.setTimeout
    2.AJAX(即:XMLHttpRequest)
    3.AddEventListener
  • 返回值在这三个东西内部,那么这个函数就是异步函数
    还有其他API,现在还没说明
  • AJAX可以设置为同步?
    可以,但这样做会让页面卡住,因为同步的话在请求之后页面2s期间什么都不能做,做了也没反应,浏览器就傻等着得到结果才会进行下一步。

4.2 摇骰子

  • 代码
function 摇骰子(){
  setTimeout(()=>{//箭头函数
    return parseInt(Math.random()*6)+1 //return一个1~6的数
  },1000)
}
  • 分析
    1.摇骰子() 函数没有写 return,那就是 return undefined
    2.箭头函数里有return,返回真正的结果
    3.所以这是一个异步函数/异步任务

4.3 摇骰子(续)

  • 添加代码
const n = 摇骰子()
console.log(n)
  • 得到的结果是:undefined(因为摇骰子函数没有return)
  • 那如何拿到异步的结果?
  • 答:用回调。写一个函数,然后把函数的地址给摇骰子。
    一个很明显的回调特征:声明了一个函数f1,自己不调,给别人调
function f1(x){ console.log(x) }
摇骰子(f1)
  • 要求把摇骰子函数得到结果后,把结果作为参数传给f1,并且调用f1
function 摇骰子(fn){
  setTimeout(()=>{//箭头函数
    fn(parseInt(Math.random()*6)+1) //return一个1~6的数
  },1000)
}

4.4 摇骰子(简化)

  • 摇骰子函数必须调用f1
  • 简化箭头函数
    由于 f1 声明之后只用了一次,可以删掉 f1
    用两次才需要声明,用一次可以直接用匿名函数
  • 摇骰子(x=>{console.log(x)})还可以简化为摇骰子(console.log)
    注意:如果参数个数前后不一致,就不能这样简化,简化后默认前后参数一样
  • 自调用:一个函数后面加了括号表示调用
    一个函数后面没有括号,就表示没有调用

4.5 一个刁钻的面试题(错误的参数简化)

const array = ['1','2','3'].map(parseInt)
console.log(array)

在这里插入图片描述

  • 注:
    1.parseInt(string, radix) 解析一个字符串并返回指定基数的十进制整数, radix 是2-36之间的整数,表示被解析字符串的基数。(如:radix 为2,表示二进制)
    2.map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
  • 简化前的正确代码为:
const array = ['1','2','3'].map((item,i,arr)=>{//参数为:item、下标、原数组
  return parseInt(item)
})
console.log(array)
  • 由于简化后,默认前后参数一致,因此系统默认:parseInt(item,i,arr)
  • 但 parseInt 最多接收 2 个参数,arr 被忽略,而下标 i 被当做 radix 基数来使用
parseInt('1',0,arr) => 1 //arr被忽略,0为无效参数,则结果为1
parseInt('2',1,arr) => NAN //2被当做1进制数解析为十进制,则2不合法 
parseInt('3',2,arr) => NAN //3被当做二进制数解析,不合法
  • 因此:永远使用箭头函数,不要随便简化
const array = ['1','2','3'].map(item => parseInt(item))
console.log(array)

5.总结

  • 异步任务不能拿到结果
  • 于是传一个回调给异步任务
  • 异步任务完成时调用回调
  • 调用的时候把结果作为参数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值