转行学前端的第 56 天 : 了解 BOM window 定时器

1,212 阅读8分钟

我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。

今日学习目标

昨天主要基于搜索基础学习 BOM window 对象控制窗体移动大小变化 ,今天主要开始基于搜索基础学习单线程模型 和定时器相关,又是适合学习的一天,加油,小又又!!!!


今日学习概要

  • 单线程模式
  • window.setTimeout()
  • window.clearTimeout()
  • window.setInterval()
  • window.clearInterval()

单线程模型

基础说明

单线程模型指的是,JavaScript 只在一个线程上运行。也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待。


单线程运行与引擎线程

注意,JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程。

事实上,JavaScript 引擎有多个线程,单个脚本只能在一个线程上运行(称为主线程),其他线程都是在后台配合。


历史原因

JavaScript 之所以采用单线程,而不是多线程,跟历史有关系。JavaScript 从诞生起就是单线程,原因是不想让浏览器变得太复杂.

因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。

如果 JavaScript 同时有两个线程,一个线程在网页 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?是不是还要有锁机制?

所以,为了避免复杂性,JavaScript 一开始就是单线程,这已经成了这门语言的核心特征,将来也不会改变。


好处

这种模式的好处是实现起来比较简单,执行环境相对单纯;

单线程模型虽然对 JavaScript 构成了很大的限制,但也因此使它具备了其他语言不具备的优势。

如果用得好,JavaScript 程序是不会出现堵塞的,这就是为什么 Node 可以用很少的资源,应付大流量访问的原因。

为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以,这个新标准并没有改变 JavaScript 单线程的本质。


坏处

坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。

常见的浏览器无响应(假死),往往就是因为某一段 JavaScript 代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

JavaScript 语言本身并不慢,慢的是读写外部数据,比如等待 Ajax 请求返回结果。这个时候,如果对方服务器迟迟没有响应,或者网络不通畅,就会导致脚本的长时间停滞。

如果排队是因为计算量大,CPU 忙不过来,倒也算了,但是很多时候 CPU 是闲着的,因为 IO 操作(输入输出)很慢(比如 Ajax 操作从网络读取数据),不得不等着结果出来,再往下执行。

JavaScript 语言的设计者意识到,这时 CPU 完全可以不管 IO 操作,挂起处于等待中的任务,先运行排在后面的任务。

等到 IO 操作返回了结果,再回过头,把挂起的任务继续执行下去。这种机制就是 JavaScript 内部采用的“事件循环”机制(Event Loop)。


window.setTimeout()

基础语法

var timeoutID = scope.setTimeout(function[, delay, arg1, arg2, ...]);
var timeoutID = scope.setTimeout(function[, delay]); 
var timeoutID = scope.setTimeout(code[, delay]);

参数说明

  • function function 是你想要在到期时间(delay毫秒)之后执行的函数。

  • code

这是一个可选语法,你可以使用字符串而不是function ,在delay毫秒之后编译和执行字符串 (使用该语法是不推荐的, 原因和使用 eval()一样,有安全风险)。

  • delay 可选 延迟的毫秒数 (一秒等于1000毫秒),函数的调用会在该延迟之后发生。如果省略该参数,delay取默认值0,意味着“马上”执行,或者尽快执行。不管是哪种情况,实际的延迟时间可能会比期待的(delay毫秒数) 值长,原因请查看实际延时比设定值更久的原因:最小延迟时间。

  • arg1, ..., argN 可选 附加参数,一旦定时器到期,它们会作为参数传递给function


返回值

返回值timeoutID是一个正整数,表示定时器的编号。这个值可以传递给clearTimeout()来取消该定时器。

需要注意的是setTimeout()setInterval()共用一个编号池,技术上,clearTimeout()clearInterval() 可以互换。但是,为了避免混淆,不要混用取消定时函数。

在同一个对象上(一个window或者worker),setTimeout()或者setInterval()在后续的调用不会重用同一个定时器编号。但是不同的对象使用独立的编号池。


详细说明

WindowOrWorkerGlobalScope 混合的 setTimeout()方法设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。


案例

下文的例子在网页中设置了两个简单的按钮,以触发 setTimeout() 和 clearTimeout() 方法:按下第一个按钮会设置一个定时器,定时器在 2s 后显示一个警告对话框,并将此次 setTimeout 的定时器 ID 保存起来,按下第二个按钮可以取消定时器。

<p>Live Example</p>
<button onclick="delayedAlert();">Show an alert box after two seconds</button>
<p></p>
<button onclick="clearAlert();">Cancel alert before it happens</button>

var timeoutID;

function delayedAlert() {
  timeoutID = window.setTimeout(slowAlert, 2000);
}

function slowAlert() {
  alert('That was really slow!');
}

function clearAlert() {
  window.clearTimeout(timeoutID);
}

真的是等了两秒左右,才出现的一个弹窗~~~~~


window.clearTimeout()

基础语法

scope.clearTimeout(timeoutID)

参数说明

  • timeoutID

要取消定时器的标识符。 该ID由相应的setTimeout()调用返回。

值得注意的是,setTimeout()和setInterval()使用共享的ID池, 这意味着你可以在技术上交替使用clearTimeout()和clearInterval() 。 但是,为了清楚起见,你应该避免这样做。


详细说明

WindowOrWorkerGlobalScope内置的clearTimeout()方法取消了先前通过调用setTimeout()建立的定时器。


案例

在一个网页中运行如下脚本,并且点击一次页面。一秒钟后你会看见弹出一条信息。如果你在一秒内不停点击页面,弹出框将不再出现。

var alarm = {
  remind: function(aMessage) {
    alert(aMessage);
    delete this.timeoutID;
  },

  setup: function() {
    this.cancel();
    var self = this;
    this.timeoutID = window.setTimeout(function(msg) {self.remind(msg);}, 1000, "Wake up!");
  },

  cancel: function() {
    if(typeof this.timeoutID == "number") {
      window.clearTimeout(this.timeoutID);
      delete this.timeoutID;
    }
  }
};
window.onclick = function() { alarm.setup() };

哇这个效果可以~~~


window.setInterval()

基础语法

var intervalID = scope.setInterval(func, delay, [arg1, arg2, ...]);
var intervalID = scope.setInterval(code, delay);

参数说明

  • function function 是你想要在到期时间(delay毫秒)之后执行的函数。

  • code

这是一个可选语法,你可以使用字符串而不是function ,在delay毫秒之后编译和执行字符串 (使用该语法是不推荐的, 原因和使用 eval()一样,有安全风险)。

  • delay 可选 延迟的毫秒数 (一秒等于1000毫秒),函数的调用会在该延迟之后发生。如果省略该参数,delay取默认值0,意味着“马上”执行,或者尽快执行。不管是哪种情况,实际的延迟时间可能会比期待的(delay毫秒数) 值长,原因请查看实际延时比设定值更久的原因:最小延迟时间。

  • arg1, ..., argN 可选 附加参数,一旦定时器到期,它们会作为参数传递给function


返回值

返回值timeoutID是一个正整数,表示定时器的编号。这个值可以传递给clearInterval()来取消该定时器。

需要注意的是setTimeout()setInterval()共用一个编号池,技术上,clearTimeout()clearInterval() 可以互换。但是,为了避免混淆,不要混用取消定时函数。

在同一个对象上(一个window或者worker),setTimeout()或者setInterval()在后续的调用不会重用同一个定时器编号。但是不同的对象使用独立的编号池。


详细说明

WindowOrWorkerGlobalScope 的 setInterval() 方法重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟。

在Window 和 Worker 接口上提供的setInterval()方法,重复调用函数或执行代码段,每次调用之间有固定的时间延迟。

它返回一个唯一标识时间间隔的时间间隔ID,因此可以稍后通过调用clearInterval()将其删除。此方法由WindowOrWorkerGlobalScope mixin定义。


案例

var intervalID = window.setInterval(myCallback, 500, 'Parameter 1', 'Parameter 2');

function myCallback(a, b)
{
 // Your code here
 // Parameters are purely optional.
 console.log(a);
 console.log(b);
}

window.clearInterval()

基础语法

scope.clearInterval(intervalID)

参数说明

  • intervalID

要取消的定时器的 ID。是由 setInterval() 返回的。

值得一提的是,setInterval() 和 setTimeout() 共用其定义的 IDs,即可以使用 clearInterval() 和 clearTimeout() 中的任意一个。然而,为了使代码可读性更强,你应该尽量避免这种用法。


详细说明

WindowOrWorkerGlobalScope mixin 的 clearInterval() 方法可取消先前通过 setInterval() 设置的重复定时任务。

一般和setInterval() 一起使用~~~~


今日学习总结


今日心情

今天主要基础了解了一下基础学习单线程模型 和定时器相关,感觉效果很酷,不过在线程和任务这块没什么理解,希望明天学习更多~~~~

本文使用 mdnice 排版