javascript的成熟分类(【JavaScript】【5】定时器(包含回调函数与Promise))
前言
什么是定时器
JavaScript提供定时执行代码的功能 ,叫做定时器(timer) ,主要由setTimeout()和setInterval()这两个函数来完成 。它们向任务队列添加定时任务 。了解回调函数和Promise对象
一 、回调函数
你不知道用户何时单击按钮 。 因此 ,为点击事件定义了一个事件处理程序 。 该事件处理程序会接受一个函数 ,该函数会在该事件被触发时被调用 。 回调是一个简单的函数 ,会作为值被传给另一个函数 ,并且仅在事件发生时才被执行 。 之所以这样做 ,是因为 JavaScript 具有顶级的函数 ,这些函数可以被分配给变量并传给其他函数(称为高阶函数) 。通常会将所有的客户端代码封装在 window 对象的 load 事件监听器中 ,其仅在页面准备就绪时才会运行 window.addEventListener(load, () => { //window 已被加载 。 //做需要做的 。 }) 回调无处不在 ,不仅在 DOM 事件中 。一个常见的示例是使用定时器: setTimeout(() => { // 2 秒之后运行 。 }, 2000) XHR 请求也接受回调 ,在此示例中 ,会将一个函数分配给一个属性,该属性会在发生特定事件(在该示例中 ,是请求状态的改变)时被调用: const xhr = new XMLHttpRequest() xhr.onreadystatechange = () => { if (xhr.readyState === 4) { xhr.status === 200 ? console.log(xhr.responseText) : console.error(出错) } } xhr.open(GET, http://nodejs.cn) xhr.send() 回调适用于简单的场景!但是 ,每个回调都可以添加嵌套的层级,并且当有很多回调时 ,代码就会很快变得非常复杂 window.addEventListener(load, () => { document.getElementById(button).addEventListener(click, () => { setTimeout(() => { items.forEach(item => { //你的代码在这里 。 }) }, 2000) }) })这是一个简单的四级嵌套 ,但是当嵌套越来越复杂时 ,不推荐使用多级嵌套。从 ES6 开始 ,JavaScript 引入了一些特性 ,可以帮助处理异步代码而不涉及使用回调:Promise(ES6)和 Async/Await(ES2017) 。
二 、 Promise
promise对象
Promise对象是CommonJS工作组提出的一种规范 ,目的是为异步操作提供统一接口 。
首先 ,它是一个对象 ,也就是说与其他JavaScript对象的用法 ,没有什么两样;其次 ,它起到代理作用(proxy) ,充当异步操作与回调函数之间的中介。它使得异步操作具备同步操作的接口 ,使得程序具备正常的同步运行的流程,回调函数不必再一层层嵌套 。
简单说 ,它的思想是 ,每一个异步任务立刻返回一个Promise对象,由于是立刻返回 ,所以可以采用同步操作的流程 。这个Promises对象有一个then方法 ,允许指定回调函数 ,在异步任务完成后调用 。 异步操作f1返回一个Promise对象 ,它的回调函数f2写法如下 (new Promise(f1)).then(f2) 多层回调函数 //传统写法 step1(function(value1){ step2(value1,function(value2){ step3(value2,function(value3){ step4(value3,function(value4){ ... }) }) }) }) //Promise写法 (new Promise(step1)) .then(step2); .then(step3); .then(step4);总的来说 ,传统的回调函数写法使得代码混成一团 ,变得横向发展而不是向下发展 。Promises规范就是为了解决这个问题而提出的 ,目标是使用正常的程序流程(同步) ,来处理异步操作 。它先返回一个Promise对象 ,后面的操作以同步的方式 ,寄存在这个对象上面 。等到异步操作有了结果 ,再执行前期寄放在它上面的其他操作 。
Promise对象的三种状态:
异步操作“未完成 ”(pending)
异步操作“已完成 ”(resolved ,又称fulfilled)
异步操作“失败 ”(rejected)
三种途径的变换方式只有两种:从未完成到已完成和从未完成到失败 变换方式只能发生一次,一旦状态变为"已完成"或"失败" ,就意味着不会发生新的状态变化了 。Promise对象的最终状态只有两种
异步操作成功:Promise对象传回一个值 ,状态变为resolved
异步操作失败,Promise对象抛出一个错误 ,状态变为rejected // po是一个Promise对象 //Promise对象po使用then方法绑定两个回调函数 ,操作成功返回console.log ,操作失败返回console.error ,这两个函数都接受异步操作传回的值作为参数 po.then( console.log, console.error ); then方法可以链式使用 po .then(step1) .then(step2) .then(step3) .then{ console.log; console.error; //Promise对象的错误有传递性 }从同步角度看等价于
try { var v1 = step1(po); var v2 = step2(v1); var v3 = step3(v2); console.log(v3); } catch (error) { console.error(error); }Promise对象的生成
var promise = new Promise(function(resolve,reject){ if(/*异步操作成功*/){ resolve(value); //在异步操作成功时调用 ,并将异步操作的结果 ,作为参数传递出去 }else{ reject(error); //在异步操作失败时调用 ,并将异步操作报出的错误 ,作为参数传递出去 } })可以使用then方法
po.then(function(value){ //success },function(value){ //failure });加载图片写成一个Promise
var preloadImage = function(path){ return new Promise(function(resolve,reject){ var image = new Image(); image.onload = resolve; image.onerror = reject; image.src = path; }) }三 、定时器与清除定时器的方法
1
推迟执行的代码必须以字符串的形式 ,放入setTimerout
因为引擎内部使用eval函数 ,将字符串转化为代码 。 setTimeout(console.log(1),1000)2
如果推迟执行的是函数 ,则可以直接将函数名 ,放入setTimeout 。
一方面eval函数有安全顾虑,另一方面为了便于JavaScript引擎优化代码 ,setTimeout方法一般总是采用函数名的形式 function fn(){ console.log(2); } setTimeout(fn,1000); //或者 setTimeout(function(){ console.log(2); },1000)3 清除定时器
function fn(){ console(2); } var timer = setTimeout(fn,1000); clearTimeout(timer);四 、京东购物车倒计时案例
布局省略代码如下(示例):
var day = document.querySelector(.day) var hour = document.querySelector(".hour"); var minute = document.querySelector(.minute); var second = document.querySelector(".second"); var inputTime = +new Date(2022-6-4 22:00:00) countDown(); var timer = null; timer = setInterval(countDown,1000); var button2 = document.querySelector(.start); button2.addEventListener(click,()=>{ timer = setInterval(countDown,1000); }) var button = document.querySelector(.stop); button.addEventListener(click,function(){ clearTimeout(timer); }) function countDown(){ var nowTime = +new Date(); //返回当前毫秒数 var time = (inputTime - nowTime) / 1000; //时间差 var d = parseInt(time / 60 / 60 /24); d = d < 10 ? 0+d : d; day.innerHTML = d; var h = parseInt(time / 60 / 60 %24); h = h < 10? 0+h : h; hour.innerHTML = h; var m = parseInt(time / 60 % 60); m = m < 10? 0+m : m; minute.innerHTML = m; var s = parseInt(time % 60) s = s <10? 0+s : s; second.innerHTML = s; }五 、发送验证码案例
布局省略代码如下(示例):
//1 按钮点击后 ,禁用按钮 //2 同时按钮里面的内容会变化,主要button里面的内容通过innerHTML修改 //3 秒数定义一个变量在定时器里不断递减 //4 如果变量为0说明时间到了 ,停止计数器 ,并复原按钮初始状态 var button = document.querySelector(button); var time = 10; button.addEventListener(click,function(){ button.disabled = true; var timer = setInterval(()=>{ if(time == 0){ //清除定时器和复原按钮 clearInterval(timer); button.disabled = false; button.innerHTML = 发送; time = 10; }else{ button.innerHTML = 还剩+time+秒发送; --time; } },1000) }) // while(time == 0){ // button.disabled = false; // button.innerHTML = 发送验证码; // }尽量不去使用while总结
提示:这里对文章进行总结:
Promise对象还需要继续学习 。定时器要学会计数和清除创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!