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

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

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)”。

问关于异步、主线程、事件循环(Event Loop)的时候:按照下面回答:

面试官问问题:js是单线程模式的,那么他是怎么实现异步操作的?

答:

js里面的任务分为同步任务和异步任务

同步任务进入主线程一个一个得执行。异步任务进入任务队列,等同步任务执行完了之后,只有触发了某个条件,才把任务队列里面的任务放到主线程执行(比如ajax得到返回的数据,就开始执行回调函数,setTimeOut的时间到了,就执行回调函数)

异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。

同步任务执行完之后,引擎就一遍一遍得检查。JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环(Event Loop)。事件循环机制一遍一遍得检查,符合条件,就把异步任务放到主线程里面去执行(比如ajax返回的数据到了,setTimeOut里面的时间到了)

异步操作的方法:

1.回调函数

2.事件监听(触发条件,执行回调函数)

3.ES6:Promise

定时器

它们向任务队列添加定时任务。时间到就添加,然后事件循环就会扫到,扫到了就执行里面的回调函数。

异步操作

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

如果代码这样写:

function setClock(){

console.log('1定一个闹钟,三秒钟之后响');

setTimeout(()=>{

console.log('2三秒到了,闹钟响了!');

},3000)

}

function getUp(){

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

}

setClock();//定闹钟

getUp();//起床

结果:

f44f9bc2c24f8b347db93c068fbcba88.png

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

异步就是不等结果,直接进行下一步.

setClock();//定闹钟执行完了之后直接进行下一步getUp();//起床

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

解决方法是使用回调函数:

回调是拿到异步结果的一种方式

(其实回调也可以拿同步结果)

举一个例子:

同步:我让黄牛去买票,我站着等他买好票再给我,然后再去做别的.

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

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

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

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

使用回调函数

function setClock(callBack){

console.log('1定一个闹钟,三秒钟之后响');

setTimeout(()=>{

console.log('2三秒到了,闹钟响了!');

callBack();

},3000)

}

function getUp(){

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

}

setClock(getUp);

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

区分同步和异步

562fefc21c4c7e273c7d20a6510d41b5.png

就是因为有了setTimeout才算异步

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

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

8c2d24634fb9ee4f5ded04e481cb78fa.png

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

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

如下:

f153a226e20250df832aeb314a52a668.png

所以就可以使用promise.then(success,error)承诺成功之后执行success函数,承诺失败后执行error函数.

这个success,error就是callBack(回调函数),这个Promise(承诺)就是异步任务

promise就是知道没法得到结果,那我就要你一个承诺,要承诺好拿到结果后要做什么事.

所以$.ajax()返回的结果是一个承诺,不是结果,因为结果还没有到来

使用回调函数

使用回调要用这样的形式

fn(参数1,参数2,()=>{

回调函数(xxx,xxx,()=>{})

})

不要用

fn(参数1,参数2,回调函数(xxx,xxx))

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

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

be353da90c2a2bc50b140f0b2a21ea3c.png

函数的作用域链

先看面试题

题目1

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

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

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

解密

函数在执行的过程中,先从自己内部找变量

如果找不到,再从创建当前函数所在的作用域去找, 以此往上

注意找的是变量的当前的状态

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值