JS定时器的坑坑挖挖

JS定时器

首先了解什么是同步代码?什么是异步代码?同步代码和异步代码的关系是什么呢?

1、同步代码:

  代码单线执行,发送服务器请求后,等待返回数据,会出现网页空白(阻塞网页运行)(通俗讲,就是代码按照顺序执行,但凡上面代码没有执行完,下边代码不能执行

2、异步代码:

  代码发送请求后继续执行后续代码,不等待服务器返回数据(网页运行流畅)
js中常见的异步执行代码:
1.ajax请求:异步 JavaScript 和 XML
2.定时器:间隔一段时间才会执行,
3.事件处理函数:满足事件触发条件才会执行
异步底层原理:
  js代码是单线执行,代码从上往下依次执行,当遇到【异步任务】时,将其储存到一个【执行队列】中,当主线的代码执行结束后,在根据服务器的处理先后顺序执行剩余的【异步任务】

2、同步代码和异步代码的关系
1、JS中执行永远是同步代码执行完,异步代码才会执行
2、异步代码存放的位置是:在栈中,堪称两个队列
3、异步代码的特点:①完全不阻塞页面的渲染和js的代码的执行

3、面试题目alert是否会打印?

<script type="text/javascript">
var t=true;
window.setTimeout(function(){
t=false;
},1000);
while(t){

}
alert('end');
</script>

首先答案:不会打印
  这段代码,setTimeout是异步线程,需要等待js引擎处理完同步代码(while语句)之后才会执行,while语句直接是个死循环,js引擎没有空闲,不会执行下面的alert,也不会插入setTimeout。所以即使这个时候你把setTimeout的时间改成0,他还是不会执行。

JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序

一、浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。

  1. javascript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。

  2. GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

  3. 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。(当线程中没有执行任何同步代码的前提下才会执行异步代码)

4、js 定时器有以下两个方法和取消定时器的两个方法:

4.1、创建定时器

(1)setTimeout

第一个参数:需要等待执行的内容,回调函数
第二个参数:等待时间以ms为单位(1000ms=1s)
返回值:定时器的编号

(2)setInterval()

第一个参数:需要等待执行的内容,回调函数
第二个参数:间隔时间(1000ms=1s),每隔间隔时间执行一次(同步代码执行完之后)
返回值:

4.2、取消/清除定时器

(1)clearTimeout()//清除setTimeout定时器

参数;①接收定时器的编号

(2)clearInterval()//清除setInterval定时器

参数;①接收定时器的编号②定时器的结束时间/定时器的执行时长

<body>
<button id="btn1">Btn1</button>
<button id="btn2">Btn2</button>

<script>
    let timer = null
    let btn1 = document.querySelector("#btn1")

    let btn2 = document.querySelector("#btn2")
    btn1.onclick = function (){
        timer = setTimeout(function (){
            console.log("fasfasf")
        },5000)
    }
    btn2.onclick = function (){
        clearTimeout(timer)
    }
</script>
</body>

规定每一秒进行打印一个i++,直到第十秒停止用setInterval

  // 方法一:每执行一次定时器内部都进行判断,当10s的时候进行清楚
   let i = 0
   let timer = setInterval(function (){
       console.log(i++)
       if(i>10){
           clearInterval(timer)
       }
   },1000)
   //方法二:直接设置一个setInterval定时器,然后设置一个清除定时器,10s后清除
    let j=0
    let timer2 = setInterval(function (){
        console.log(j++)
    })

4.3、区别:

setTimeout只在指定时间后执行一次,代码如下:
<script>
//定时器 异步运行
function hello(){
alert("hello");
}
//使用方法名字执行方法
let t1 = window.setTimeout(hello,1000);
let t2 = window.setTimeout("hello()",3000);//使用字符串执行方法
let t2 = window.setTimeout(function(){
alert("hellopppp");
},3000);//直接写入回调函数
window.clearTimeout(t1);//去掉定时器
</script>

setInterval以指定时间为周期循环执行,代码如下:

function refreshQuery(){
   $("#mainTable").datagrid('reload',null);
}
//实时刷新时间单位为毫秒
setInterval('refreshQuery()',8000); //每隔8秒刷新一次,停不下来
/* 刷新查询 */

5、Promise异步变同步

5.1Promise的出现,原本是为了解决回调地狱的问题

5.2地狱函数:

在使用JavaScript时,为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱。比如说你要把一个函数 A 作为回调函数,但是该函数又接受一个函数 B 作为参数,甚至 B 还接受 C 作为参数使用,就这样层层嵌套,人称之为回调地狱,代码阅读性非常差。比如:

var sayhello = function (name, callback) {
  setTimeout(function () {
    console.log(name);
    callback();
  }, 1000);
}
sayhello("first", function () {
  sayhello("second", function () {
    sayhello("third", function () {
      console.log("end");
    });
  });
});
//输出: first second third  end

5.3、解决回调地狱

解决回调地狱有很多方法,比如:Promise 对象、Generator 函数、async 函数

5.4、Promise 对象解决回调地狱

采用链式的 then,可以指定一组按照次序调用的回调函数。这时,前一个 then 里的一个回调函数,返回的可能还是一个 Promise对象(即有异步操作),这时后一个回调函数,就会等待该 Promise对象的状态发生变化,才

会被调用。由此实现异步操作按照次序执行。
var sayhello = function (name) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(name);
      resolve();  //在异步操作执行完后执行 resolve() 函数
    }, 1000);
  });
}
sayhello("first").then(function () {
  return sayhello("second");  //仍然返回一个 Promise 对象
}).then(function () {
  return sayhello("third");
}).then(function () {
  console.log('end');
}).catch(function (err) {
  console.log(err);
})
//输出:first  second  third end

上面代码中,第一个 then 方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就继续执行第二个 then 里的回调函数

5.5、Promise的特点:

resolve表示代码执行成功,reject表示代码执行失败。Promise是一个构造函数,构造函数创建对象的时候参数是一个回调函数,里边的参数是resolve和reject,这两个都是函数。
1、接收一个回调函数(参数)
2、回调函数是第一段异步操作
3、回调函数有resolve和reject两个函数形参
4、resolve,意味着紧挨着的then执行
5、reject,意味着.catch执行

5.6、怎么样进行顺序执行多个异步任务?

1.直接then链式调用 不多说
2.利用Promise.resolve()+forEach循环/reduce循环,将promise串成一任务队列,本质上是简化的链式调用

let a = function() {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            console.log('11111111111',)
            resolve('第一个异步进程')
        },3000)
    })
} 
let b = function(){
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            console.log('2222222222' )
            resolve('第二个异步进程')
        },2000)
    })
}
let c = function(){
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            console.log('3333333333333')
            resolve('第三个异步进程')
        },1000)
    })
}

reduce构建异步队列

let promiseArr = [a, b, c]
promiseArr.reduce((prev, cur)=>{
    return prev.then(() => {
            return cur().then(res => {
                console.log( res)
            })
    })
}, Promise.resolve())

本质上相当于

Promise.resolve().then(res => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            console.log('11111111111', )
            resolve('第一个异步进程')
        },3000)
    })
    // 只有当第一个then返回了promise对象才会接着调用下一个的then方法
}).then(() => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            console.log('2222222222', )
            resolve('第二个异步进程')
        },2000)
    })
}).then(() => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            console.log('33333333', )
            resolve('第三个异步进程')
        },1000)
    })
})

forEach构建异步队列

function fun(arr){
    let res = Promise.resolve()
    arr.forEach(item => {
        res = res.then(item)
    })
}
fun([a,b,c]).then( res => {
    console.log(res)
})
 
//相当于
Promise.resolve().then(
    a
).then(
    b
).then(
    c
)

3.async await实现

let arr = [a, b, c]
async function fun(arr){
    for(const item of arr){
        const res = await item().then()
        console.log(res)
    }
}
fun(arr)

案例

//===========做饭==============
function cook(){
    console.log('开始做饭。');
    var p = new Promise(function(resolve, reject){        //做一些异步操作
        setTimeout(function(){
            console.log('做饭完毕!');
            resolve('鸡蛋炒饭');
        }, 1000);
    });
    return p;
}
 
//===========吃饭=============================
function eat(data){
    console.log('开始吃饭:' + data);
    var p = new Promise(function(resolve, reject){        //做一些异步操作
        setTimeout(function(){
            console.log('吃饭完毕!');
            resolve('一块碗和一双筷子');
        }, 2000);
    });
    return p;
}
 
cook()
.then(function(data){
//===============通过抛出异常的方式将cook的状态设置为reject================
    throw new Error('米饭被打翻了!');
    eat(data);
})
.catch(function(data){
    //=============捕获reject状态后的异常信息==============
    console.log(data);
});
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值