1、通过使用 JavaScript,设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行。我们称之为计时事件
1、setInterval() 间隔指定的毫秒数不停地执行指定的代码
2、一般setInterval() 是用于显示时间
1、clearInterval() 方法用于停止 setInterval() 方法执行的函数代码
1、经过3秒出现新的警告框,这将一直执行
JavaScript里,我们已经会使用一些原生提供的方法来实现需要延时执行的操作代码,比如很多在线时钟的制作,图片轮播的实现,还有一些广告弹窗,但凡可以自动执行的东西,都是可以和定时器有关的。今天就来和大家分享一下,关于我们在JavaScript里经常会使用到的定时器方法
在JavaScript里,我们要学习四个定时器的使用方法,setTiemout、setInterval、setImmediate、requestAnimationFrame,一起来看看吧!
JavaScript中提供了一些原生的函数方法来实现延时去执行某一段代码,这个就是定时器
下面我们来认识一下这些定时器
setTimeout:
设置一个定时器,在定时器到期后执行一次函数或代码段
var timeoutId=window.setTimeout(func[, delay,param1,...]); var timeoutId=window.setTimeout(code[, delay]);上面用到的关键词名称的意义:
timeoutId: 定时器ID
func: 延迟后执行的函数
code: 延迟后执行的代码字符串,不推荐使用原理类似eval()
delay: 延迟的时间(单位:毫秒),默认值为0
param1: 向延迟函数传递而外的参数,IE9以上支持
setInterval:
以固定的时间间隔重复调用一个函数或者代码段
var intervalId=window.setInterval(func, delay[, param1,...]); var intervalId=window.setInterval(code, delay);intervalId: 重复操作的ID
func: 延迟调用的函数
code: 代码段
delay: 延迟时间,没有默认值
setImmediate:
在浏览器完全结束当前运行的操作之后立即执行指定的函数(仅IE10和Node 0.10+中有实现),类似setTimeout(func, 0)
var immediateId=setImmediate(func[, param1, param2, ...]); var immediateId=setImmediate(func);immediateId: 定时器ID
func: 回调
requestAnimationFrame:
专门为实现高性能的帧动画而设计的API,但是不能指定延迟时间,而是根据浏览器的刷新频率而定(帧)
var requestId=window.requestAnimationFrame(func);func: 回调
基本用法
// 下面代码执行之后会输出什么?
var intervalId, timeoutId;
timeoutId=setTimeout(function() {
console.log(1);
}, 300);
setTimeout(function() {
clearTimeout(timeoutId);
console.log(2);
}, 100);
setTimeout('console.log("5")', 400);
intervalId=setInterval(function() {
console.log(4);
clearInterval(intervalId);
}, 200);
// 分别输出: 2、4、5setInterval 和 setTimeout的区别?
// 执行下面的代码块会输出什么?
setTimeout(function() {
console.log('timeout');
}, 1000);
setInterval(function() {
console.log('interval')
}, 1000);
// 输出一次 timeout,每隔1S输出一次 interval
/*--------------------------------*/
// 通过setTimeout模拟setInterval 和 setInterval有啥区别么?
varcallback=function() {
if(times++ > max) {
clearTimeout(timeoutId);
clearInterval(intervalId);
}
console.log('start', Date.now() - start);
for(var i=0; i < 990000000; i++) {}
console.log('end', Date.now() - start);
},
delay=100,
times=0,
max=5,
start=Date.now(),
intervalId, timeoutId;
functionimitateInterval(fn, delay) {
timeoutId=setTimeout(function() {
fn();
if(times <=max) {
imitateInterval(fn ,delay);
}
}, delay);
}
imitateInterval(callback, delay);
intervalId=setInterval(callback, delay);如果是setTimeout和setInterval的话,它俩仅仅在执行次数上有区别,setTimeout一次、setIntervaln次。
而通过setTimeout模拟的setInterval与setInterval的区别则在于:setTimeout只有在回调完成之后才会去调用下一次定时器,而setInterval则不管回调函数的执行情况,当到达规定时间就会在事件队列中插入一个执行回调的事件,所以在选择定时器的方式时需要考虑setInterval的这种特性是否会对你的业务代码有什么影响?
setTimeout(func, 0) 和 setImmediate(func)谁更快?
console.time('immediate');
console.time('timeout');
setImmediate(()=> {
console.timeEnd('immediate');
});
setTimeout(()=> {
console.timeEnd('timeout');
}, 0);在Node.JS v6.7.0中测试发现setTimeout更早执行
下面代码运行后的结果是什么?
// 题目一
var t=true;
setTimeout(function(){
t=false;
}, 1000);
while(t){}
alert('end');
// 题目二
for(var i=0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
// 题目三
var obj={
msg: 'obj',
shout: function() {
alert(this.msg);
},
waitAndShout: function() {
setTimeout(function() {
this.shout();
}, 0);
}
};
obj.waitAndShout();在讲解上面面试题的答案之前,我们先要理解一下定时器的工作原理,以方便理解上面的题目
这里将用引用How JavaScript Timers Work中的例子来解释定时器的工作原理,该图为一个简单版的原理图。
在这图中,左侧数字代表时间,单位毫秒;
左侧文字代表某一个操作完成后,浏览器去询问当前队列中存在哪些正在等待执行的操作;
蓝色方块表示正在执行的代码块;
右侧文字代表在代码运行过程中,出现哪些异步事件。
大致流程如下:
1.程序开始时,有一个JS代码块开始执行,执行时长约为18ms,在执行过程中有3个异步事件触发,其中包括一个setTimeout、鼠标点击事件、setInterval
2.第一个setTimeout先运行,延迟时间为10ms,稍后鼠标事件出现,浏览器在事件队列中插入点击的回调函数,稍后setInterval运行,10ms到达之后,setTimeout向事件队列中插入setTimeout的回调
当第一个代码块执行完成后,浏览器查看队列中有哪些事件在等待,他取出排在队列最前面的代码来执行
3.在浏览器处理鼠标点击回调时,setInterval再次检查到到达延迟时间,他将再次向事件队列中插入一个interval的回调,以后每隔指定的延迟时间之后都会向队列中插入一个回调
4.后面浏览器将在执行完当前队头的代码之后,将再次取出目前队头的事件来执行
在这也只是对定时器的工作原理做了简单的叙述,其实实际的实现处理过程会更加复杂。
在我们理解了定时器的运行原理之后,接下来我们就基于运行原理的基础上,来看看上面的经典面试题的答案
第一题
alert永远都不会执行,因为JS是单线程的,且定时器的回调将在等待当前正在执行的任务完成后才执行,而while(t) {}直接就进入了死循环一直占用线程,不给回调函数执行机会
第二题
代码会输出 5 5 5 5 5,理由同上,当i=0时,生成一个定时器,将回调插入到事件队列中,等待当前队列中无任务执行时立即执行,而此时for循环正在执行,所以回调被搁置。当for循环执行完成后,队列中存在着5个回调函数,他们的都将执行console.log(i)的操作,因为当前JS代码上中并没有使用块级作用域,所以i的值在for循环结束后一直为5,所以代码将输出5个5
第三题
这个问题涉及到this的指向问题,由setTimeout()调用的代码运行在与所在函数完全分离的执行环境上. 这会导致这些代码中包含的this关键字会指向window (或全局)对象,window对象中并不存在shout方法,所以就会报错,修改方案如下:
var obj={
msg: 'obj',
shout: function() {
alert(this.msg);
},
waitAndShout: function() {
var self=this; // 这里将this赋给一个变量
setTimeout(function() {
self.shout();
}, 0);
}
};
obj.waitAndShout();1.setTimeout有最小时间间隔限制,HTML5标准为4ms,小于4ms按照4ms处理,但是每个浏览器实现的最小间隔都不同
2.因为JS引擎只有一个线程,所以它将会强制异步事件排队执行
3.如果setInterval的回调执行时间长于指定的延迟,setInterval将无间隔的一个接一个执行
4.this的指向问题可以通过bind函数、定义变量、箭头函数的方式来解决
celery 定时器是一个调度器(scheduler);它会定时地开启(kicks off)任务,然后由集群中可用的工人(worker)来执行。
定时任务记录(entries)默认 从 beat_schedule 设置中获取,但自定义存储也可以使用,如把记录存储到SQL数据库中。
要确保同一时间一份时间表上只有一个调度器在运行,否则会因为重复发送任务而结束。使用集中途径意味着定时任务不用必须同步,并且服务无需用锁操控。
celery需要rabbitMQ、Redis、Amazon SQS、Zookeeper(测试中) 充当broker来进行消息的接收,并且也支持多个broker和worker来实现高可用和分布式。http://docs.celeryproject.org/en/latest/getting-started/brokers/index.html
版本和要求
Celery version 4.0 runs on
Python ?2.7, 3.4, 3.5?
PyPy ?5.4, 5.5?
This is the last version to support Python 2.7, and from the next version (Celery 5.x) Python 3.5 or newer is required.
If you’re running an older version of Python, you need to be running an older version of Celery:
Python 2.6: Celery series 3.1 or earlier.
Python 2.5: Celery series 3.0 or earlier.
Python 2.4 was Celery series 2.2 or earlier.
Celery is a project with minimal funding, so we don’t support Microsoft Windows. Please don’t open any issues related to that platform.环境准备
安装rabbitMQ或Redis
安装celery
pip3 install celerys1.py
s1.pyimport time
from celery import Celery
app=Celery('tasks', broker='redis://192.168.10.48:6379', backend='redis://192.168.10.48:6379')
@app.task
def xxxxxx(x, y):
time.sleep(10)
return x + ys2.py
from s1 import func
# func,并传入两个参数
result=xxxxxx.delay(4, 4)
print(result.id)s3.py
from celery.result import AsyncResult
from s1 import app
async=AsyncResult(id="f0b41e83-99cf-469f-9eff-74c8dd600002", app=app)
if async.successful():
result=async.get()
print(result)
# result.forget() # 将结果删除
elif async.failed():
print('执行失败')
elif async.status=='PENDING':
print('任务等待中被执行')
elif async.status=='RETRY':
print('任务异常后正在重试')
elif async.status=='STARTED':
print('任务已经开始被执行')# 执行 s1.py 创建worker(终端执行命令):
celery worker -A s1 -l info
# PS:Windows系统上执行命令时出错解决方法
pip3 install eventlet
# 后期运行修改为:
celery worker -A s1 -l info -P eventlet
# 执行 s2.py ,创建一个任务并获取任务ID:
python3 s2.py
# 执行 s3.py ,检查任务状态并获取结果:
python3 s3.pypro_cel
├── celery_tasks# celery相关文件夹
│ ├── celery.py # celery连接和配置相关文件
│ └── tasks.py # 所有任务函数
├── check_result.py # 检查结果
└── send_task.py # 触发任务pro_cel/celery_tasks/celery
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from celery import Celery
celery=Celery('func',
broker='redis://192.168.111.111:6379',
backend='redis://192.168.111.111:6379',
include=['celery_tasks.tasks'])
# 时区
celery.conf.timezone='Asia/Shanghai'
# 是否使用UTC
celery.conf.enable_utc=Falsepro_cel/celery_tasks/tasks.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time
from .celery import celery
@celery.task
def func(*args, **kwargs):
time.sleep(5)
return "任务结果"
@celery.task
def hhhhhh(*args, **kwargs):
time.sleep(5)
return "任务结果"pro_cel/check_result.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from celery.result import AsyncResult
from celery_tasks.celery import celery
async=AsyncResult(id="ed88fa52-11ea-4873-b883-b6e0f00f3ef3", app=celery)
if async.successful():
result=async.get()
print(result)
# result.forget() # 将结果删除
elif async.failed():
print('执行失败')
elif async.status=='PENDING':
print('任务等待中被执行')
elif async.status=='RETRY':
print('任务异常后正在重试')
elif async.status=='STARTED':
print('任务已经开始被执行')pro_cel/send_task.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import celery_tasks.tasks
# 立即告知celery去执行func任务,并传入两个参数
result=celery_tasks.tasks.func.delay(4, 4)
print(result.id)更多配置:http://docs.celeryproject.org/en/latest/userguide/configuration.html
设定时间让celery执行一个任务
import datetime
from celery_tasks.tasks import func
"""
from datetime import datetime
v1=datetime(2020, 4, 11, 3, 0, 0)
print(v1)
v2=datetime.utcfromtimestamp(v1.timestamp())
print(v2)
"""
ctime=datetime.datetime.now()
utc_ctime=datetime.datetime.utcfromtimestamp(ctime.timestamp())
s10=datetime.timedelta(seconds=10)
ctime_x=utc_ctime + s10
# 使用apply_async并设定时间
result=func.apply_async(args=[1, 3], eta=ctime_x)
print(result.id)类似于contab的定时任务
"""
celery beat -A proj
celery worker -A proj -l info
"""
from celery import Celery
from celery.schedules import crontab
app=Celery('tasks', broker='amqp://147.918.134.86:5672', backend='amqp://147.918.134.86:5672', include=['proj.s1', ])
app.conf.timezone='Asia/Shanghai'
app.conf.enable_utc=False
app.conf.beat_schedule={
# 'add-every-10-seconds': {
# 'task': 'proj.s1.add1',
# 'schedule': 10.0,
# 'args': (16, 16)
# },
'add-every-12-seconds': {
'task': 'proj.s1.add1',
'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
'args': (16, 16)
},
}注:如果想要定时执行类似于crontab的任务,需要定制 Scheduler来完成。
pro_flask_celery/
├── app.py
├── celery_tasks
├── celery.py
└── tasks.pyapp.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask
from celery.result import AsyncResult
from celery_tasks import tasks
from celery_tasks.celery import celery
app=Flask(__name__)
TASK_ID=None
@app.route('/')
def index():
global TASK_ID
result=tasks.func.delay()
TASK_ID=result.id
return "任务已经提交"
@app.route('/result')
def result():
global TASK_ID
result=AsyncResult(id=TASK_ID, app=celery)
if result.ready():
return result.get()
return "xxxx"
if __name__=='__main__':
app.run()celery_tasks/celery.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from celery import Celery
from celery.schedules import crontab
celery=Celery('func',
broker='redis://192.168.110.148:6379',
backend='redis://192.168.110.148:6379',
include=['celery_tasks.tasks'])
# 时区
celery.conf.timezone='Asia/Shanghai'
# 是否使用UTC
celery.conf.enable_utc=Falsecelery_task/tasks.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time
from .celery import celery
@celery.task
def hello(*args, **kwargs):
print('执行hello')
return "hello"
@celery.task
def func(*args, **kwargs):
print('执行func')
return "func"
@celery.task
def hhhhhh(*args, **kwargs):
time.sleep(5)
return "任务结果"为了定时调用任务,你必须添加记录到打点列表中:
from celery import Celery
from celery.schedules import crontab
app=Celery()
@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
# 每10秒调用 test('hello') .
sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')
# 每30秒调用 test('world')
sender.add_periodic_task(30.0, test.s('world'), expires=10)
# 每周一上午7:30执行
sender.add_periodic_task(
crontab(hour=7, minute=30, day_of_week=1),
test.s('Happy Mondays!'),
)
@app.task
def test(arg):
print(arg)用on_after_configure处理器进行这些设置意味着当使用test.s()时我们不会在模块层面运行app 。
add_periodic_task() 函数在幕后会添加记录到beat_schedule设定,同样的设定可以用来手动设置定时任务:
例子: 每30秒运行 tasks.add .
app.conf.beat_schedule={
'add-every-30-seconds': {
'task': 'tasks.add',
'schedule': 30.0,
'args': (16, 16)
},
}
app.conf.timezone='UTC'一般会使用配置文件进行配置,如下
celeryconfig.py:
broker_url='pyamqp://'
result_backend='rpc://'
task_serializer='json'
result_serializer='json'
accept_content=['json']
timezone='Europe/Oslo'
enable_utc=True
beat_schedule={
'add-every-30-seconds': {
'task': 'tasks.add',
'schedule': 30.0,
'args': (16, 16)
},
}程序里使用
app.config_from_object('celeryconfig')
注意
如果你的参数元组里只有一个项目,只用一个逗号就可以了,不要圆括号。时间表使用时间差意味着每30秒间隔会发送任务(第一个任务在celery定时器开启后30秒发送,然后上每次距一次运行后30秒发送一次)
task:要执行的任务名字
schedule:执行的频率[可以是整数秒数,时间差,或者一个周期( crontab)。你也可以自 定义你的时间表类型,通过扩展schedule接口]
args:位置参数 (list 或 tuple)
kwargs:键值参数 (dict)
options:执行选项 (dict)[这可以是任何被apply_async()支持的参数与—-exchange, routing_key, expires,等]
relative:如果 relative 是 true ,时间表“由时钟时间”安排,意味着 频率近似到最近的秒,分钟,小时或天,这取决于时间差中的时间间隔[默认relative是false,频率不会近似,会相对于celery的启动时间]
开启celery定时服务
celery -A proj beat可以把定时器嵌入到工人(worker)中,通过启用workers -B选项,如果你永远不会运行超过一个工人节点这就会很方便。但这不太常见,不推荐在生产环境这样使用
celery -A proj worker -B定时器需要在本地数据库文件(默认名为 celerybeat-schedule )存储任务上次运行时间,所以它需要在当前目录中写权限。或者你也可以给这个文件指定一个位置
celery -A proj beat -s /home/celery/var/run/celerybeat-schedule#Python##每天学python##Python入门推荐#
*请认真填写需求信息,我们会在24小时内与您取得联系。