整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript 之定時事件

          JavaScript 之定時事件

          、定時事件

          1、通過使用 JavaScript,設定的時間間隔之后來執行代碼,而不是在函數被調用后立即執行。我們稱之為計時事件

          1、設定間隔setInterval()

          1、setInterval() 間隔指定的毫秒數不停地執行指定的代碼

          2、一般setInterval() 是用于顯示時間

          2、清除間隔clearInterval()

          1、clearInterval() 方法用于停止 setInterval() 方法執行的函數代碼

          3、超時setTimeout() 方法

          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、5

          setInterval 和 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();

          在講解上面面試題的答案之前,我們先要理解一下定時器的工作原理,以方便理解上面的題目


          JS定時器的工作原理

          這里將用引用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數據庫中。

          要確保同一時間一份時間表上只有一個調度器在運行,否則會因為重復發送任務而結束。使用集中途徑意味著定時任務不用必須同步,并且服務無需用鎖操控。


        1. user:用戶程序,用于告知celery去執行一個任務。
        2. broker: 存放任務(依賴RabbitMQ或Redis,進行存儲)
        3. worker:執行任務
        4. 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 celery

          快速上手

          s1.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 + y

          s2.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.py

          多任務結構

          pro_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=False

          pro_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來完成。

          Flask中應用Celery

          pro_flask_celery/
          ├── app.py
          ├── celery_tasks
              ├── celery.py
              └── tasks.py

          app.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=False

          celery_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的啟動時間]

          Crontab 表達式語法


          開啟調度

          開啟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入門推薦#


          主站蜘蛛池模板: 久久久久无码国产精品一区 | 高清一区高清二区视频| 亚洲Av无码国产一区二区| 无码国产精品一区二区高潮| 任你躁国语自产一区在| 国产一区二区三区免费看| 亚洲欧美日韩一区二区三区在线 | 精品国产精品久久一区免费式| 丰满少妇内射一区| 日韩精品一区二区三区视频| 色窝窝免费一区二区三区| 久久se精品一区二区国产 | 精品国产天堂综合一区在线| 中文字幕精品一区二区2021年| 国产精品视频第一区二区三区| 成人免费av一区二区三区| 一区二区免费国产在线观看| 好吊妞视频一区二区| 精品国产一区二区三区在线| 精品无人乱码一区二区三区| 一区一区三区产品乱码| 亚洲熟女乱色一区二区三区| 亚洲AV成人精品一区二区三区| 亚洲欧美成人一区二区三区| 高清精品一区二区三区一区| 天堂Av无码Av一区二区三区| 精品人妻无码一区二区色欲产成人 | 一区二区免费电影| 91在线看片一区国产| 精品人无码一区二区三区| 精品无码成人片一区二区98| 呦系列视频一区二区三区| 国产乱码精品一区二区三区麻豆 | 久久久久人妻精品一区三寸| 日本一区二区三区不卡在线视频| 国产精品成人免费一区二区| 国产一区二区三区四| 精品国产一区二区三区2021| 国产剧情国产精品一区| 国产精品香蕉在线一区| 日韩精品久久一区二区三区|