前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >异步与回调/函数的作用域链

异步与回调/函数的作用域链

作者头像
代码之风
发布2018-10-31 11:32:54
1.8K0
发布2018-10-31 11:32:54
举报
文章被收录于专栏:马涛涛的专栏

异步与回调/函数的作用域链

JavaScript 只在一个线程上运行,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待。 这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。 JavaScript 语言本身并不慢,慢的是读写外部数据,比如等待 Ajax 请求返回结果。这个时候,如果对方服务器迟迟没有响应,或者网络不通畅,就会导致脚本的长时间停滞。

异步与回调

同步任务与异步任务

程序里面所有的任务,可以分成两类:同步任务(synchronous)和异步任务(asynchronous)。

同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。

异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有”堵塞“效应。

举例来说,Ajax 操作可以当作同步任务处理,也可以当作异步任务处理,由开发者决定。如果是同步任务,主线程就等着 Ajax 操作返回结果,再往下执行;如果是异步任务,主线程在发出 Ajax 请求以后,就直接往下执行,等到 Ajax 操作有了结果,主线程再执行对应的回调函数。

任务队列和事件循环

JavaScript 运行时,除了一个正在运行的主线程,引擎还提供一个任务队列(task queue),里面是各种需要当前程序处理的异步任务。(实际上,根据异步任务的类型,存在多个任务队列。为了方便理解,这里假设只存在一个队列。)

首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。

异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。如果一个异步任务没有回调函数,就不会进入任务队列,也就是说,不会重新进入主线程,因为没有用回调函数指定下一步的操作。

JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环(Event Loop)。维基百科的定义是:“事件循环是一个程序结构,用于等待和发送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。

异步操作

异步操作的模式--回调函数 有这样一个问题:

我想先定个闹钟,三秒钟后闹钟就会响.这时候我再起床.

如果代码这样写:

代码语言:javascript
复制
function setClock(){
  console.log('1定一个闹钟,三秒钟之后响');
  setTimeout(()=>{
    console.log('2三秒到了,闹钟响了!');
  },3000)
}

function getUp(){
  console.log('3闹钟已经响了,该起床了')
}
setClock();//定闹钟
getUp();//起床

结果:

getUp();//起床这个函数不会等到三秒后执行,而是会在setClock()执行后立即执行.

异步就是不等结果,直接进行下一步. setClock();//定闹钟执行完了之后直接进行下一步getUp();//起床

setClock();//定闹钟就是异步代码,不等待setClock()执行完就执行getUp(),setClock()就是异步任务

解决方法是使用回调函数: 回调是拿到异步结果的一种方式 (其实回调也可以拿同步结果) 举一个例子:

  • 同步:我让黄牛去买票,我站着等他买好票再给我,然后再去做别的.
  • 异步:我让黄牛去买票(告诉黄牛买到票就call我一下),然后我继续去做别的事

这里:我让黄牛去买票,然后我继续去做别的事就是异步,括号里的(告诉黄牛买到票就call我一下)就是回调

callBack英文有回电话的意思.就是打电话回去告诉异步结果已经得到了,可以继续依照这个结果来做下面的事了.callBack就是这个意思

代码执行完在执行下面的代码就是同步,代码没有执行完就去执行下面的代码就是异步

使用回调函数

代码语言:javascript
复制
function setClock(callBack){
  console.log('1定一个闹钟,三秒钟之后响');
  setTimeout(()=>{
    console.log('2三秒到了,闹钟响了!');
    callBack();
  },3000)
}

function getUp(){
  console.log('3闹钟已经响了,该起床了')
}

setClock(getUp);

getUp作为参数传入setClock函数,等三秒后在执行函数.getUp就是回调函数

区分同步和异步

就是因为有了setTimeout才算异步

所以我们来看看ajax.如果$.ajax()是同步的,即我们发送请求,然后等待服务器发回的响应来到之后在继续执行下面的代码,那么有什么后果:

假设我们想直接拿到请求的结果,那么我们有下面的代码:

意思就是不管请求相应多久,都等着,直到响应接收到,然后返回给这个创建的变量response.如果从发送请求到拿到相应用了2s,那么代码就停在这里了2s.

所以$.ajax()是异步的,我们拿到的只是一个承诺(Promise),我承诺会执行,并承诺会在拿到结果后执行什么什么什么 如下:

所以就可以使用promise.then(success,error)承诺成功之后执行success函数,承诺失败后执行error函数. 这个success,error就是callBack(回调函数),这个Promise(承诺)就是异步任务 promise就是知道没法得到结果,那我就要你一个承诺,要承诺好拿到结果后要做什么事. 所以$.ajax()返回的结果是一个承诺,不是结果,因为结果还没有到来

使用回调函数

使用回调要用这样的形式

代码语言:javascript
复制
fn(参数1,参数2,()=>{
    回调函数(xxx,xxx,()=>{})
})

不要用

代码语言:javascript
复制
fn(参数1,参数2,回调函数(xxx,xxx))

因为这个参数里传入的回调函数(xxx,xxx)并不是函数本身,而是运行完毕之后的返回值.

下面带我是我的一个小作品里的一部分代码,一直在嵌套回调函数.

会动的简历--完整代码地址 会动的简历--预览地址

函数的作用域链

先看面试题 题目1

代码语言:javascript
复制
var a = 1
function fn1(){
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //2

题目2

代码语言:javascript
复制
var a = 1
function fn1(){
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
function fn2(){
  console.log(a)
}
var fn = fn1()
fn() //1

题目3

代码语言:javascript
复制
var a = 1
function fn1(){

  function fn3(){
    function fn2(){
      console.log(a)
    }
    var a

    fn2()
    a = 4
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //undefined

解密

  1. 函数在执行的过程中,先从自己内部找变量
  2. 如果找不到,再从创建当前函数所在的作用域去找, 以此往上
  3. 注意找的是变量的当前的状态
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-08-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 异步与回调/函数的作用域链
    • 异步与回调
      • 同步任务与异步任务
      • 任务队列和事件循环
      • 异步操作
      • 使用回调函数
      • 区分同步和异步
      • 使用回调函数
    • 函数的作用域链
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档