GUI渲染线程
:渲染和解析HTML与css的,以及最后绘制出相应的页面。
JS引擎线程
:渲染和解析JavaScript代码的。
定时器监听线程
:监听定时器的。事件监听线程
:监听DOM事件的。HTTP网络请求线程
:发送网络请求,从服务器获取资源的。JS引擎线程
去渲染解析JavaScript代码。
代码执行流程是根据EventLoop事件循环机制来做的。
EventLoop事件循环机制涉及概念。
微任务先执行,就是每次取一个异步任务,之后就先执行这一个异步任务。
console.time("异步任务-宏任务1-2");
console.time("同步任务1");
console.time("异步任务-宏任务1-1");
console.time("异步任务-微任务");
console.time("同步任务2");
console.time("同步任务3");
console.time("异步任务-宏任务2-1");
console.time("同步任务4");
console.time("异步任务-宏任务3-1");
console.time("同步任务5");
setTimeout(() => {
console.timeEnd("异步任务-宏任务1-2");
// console.log("异步任务-宏任务1-2");
}, 20);
console.timeEnd("同步任务1");
// console.log("同步任务1");
setTimeout(() => {
console.timeEnd("异步任务-宏任务1-1");
// console.log("异步任务-宏任务1-1");
queueMicrotask(() => {
console.timeEnd("异步任务-微任务");
// console.log("异步任务-微任务");
});
}, 10);
console.timeEnd("同步任务2");
// console.log("同步任务2");
for (let i = 0; i < 90000000; i++) {
// do soming
}
console.timeEnd("同步任务3");
// console.log("同步任务3");
setTimeout(() => {
console.timeEnd("异步任务-宏任务2-1");
// console.log("异步任务-宏任务2-1");
}, 2);
console.timeEnd("同步任务4");
// console.log("同步任务4");
setTimeout(() => {
console.timeEnd("异步任务-宏任务3-1");
// console.log("异步任务-宏任务3-1");
}, 3);
console.timeEnd("同步任务5");
// console.log("同步任务5");
//----------------------------------------
//同步任务1: 0.047119140625 ms
//同步任务2: 0.14501953125 ms
//同步任务3: 68.099853515625 ms
//同步任务4: 68.2099609375 ms
//同步任务5: 68.2822265625 ms
//返回 undefined -- 这里是最后一行同步代码的返回值。
//异步任务-宏任务1-1: 68.489990234375 ms
//异步任务-微任务: 68.5400390625 ms
//异步任务-宏任务1-2: 68.593994140625 ms
//异步任务-宏任务2-1: 69.541748046875 ms
//异步任务-宏任务3-1: 71.5439453125 ms
宏任务先执行,就是每次取多个异步任务-全部微任务与宏任务,之后按顺序执行完,再去取下一次EventQueue队列中的排队队列。
EventLoop事件循环机制
setTimeout(() => {
console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
console.log(3);
}, 10);
console.log(4);
console.time("AA");
for (let i = 0; i < 90000000; i++) {
// do soming
}
console.timeEnd("AA"); //=>AA: 79ms 左右
console.log(5);
setTimeout(() => {
console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
console.log(8);
}, 15);
console.log(9);
备注
用JavaScript给定时器设置等待时间,这个等待时间是其最快执行的时间。
给定时器等待时间设置为0,也不是让其立即执行,也需要等待
等待流程为:webAPI–>EventQueue
setTimeout(() => {
console.log('定时器1')
// while (1) { }
}, 0) //定时器等待时间设置为0,也不是让其立即执行,也需要等待「WebAPI->EventQueue」
死循环会让主线程一直被占用着,那么后续所有的事件,包括异步任务的执行都将处理不了了
没死循环
console.log('start')
setTimeout(() => {
console.log('定时器1')
// while (1) { }
}, 0) //定时器等待时间设置为0,也不是让其立即执行,也需要等待「WebAPI->EventQueue」
console.log('handler....')
setTimeout(() => {
console.log('定时器2')
}, 10)
// while (1) { } //死循环会让主线程一直被占用着,那么后续所有的事情(包括异步任务的执行都将处理不了了),真实项目中千万不要出现死循环
console.log('end')
死循环在主线程-同步代码
console.log('start')
setTimeout(() => {
console.log('定时器1')
// while (1) { }
}, 0) //定时器等待时间设置为0,也不是让其立即执行,也需要等待「WebAPI->EventQueue」
console.log('handler....')
setTimeout(() => {
console.log('定时器2')
}, 10)
while (1) { } //死循环会让主线程一直被占用着,那么后续所有的事情(包括异步任务的执行都将处理不了了),真实项目中千万不要出现死循环
console.log('end')
死循环在主线程-异步代码
console.log('start')
setTimeout(() => {
console.log('定时器1')
while (1) { }//死循环会让主线程一直被占用着,那么后续所有的事情(包括异步任务的执行都将处理不了了),真实项目中千万不要出现死循环
}, 0) //定时器等待时间设置为0,也不是让其立即执行,也需要等待「WebAPI->EventQueue」
console.log('handler....')
setTimeout(() => {
console.log('定时器2')
}, 10)
console.log('end')
例子:
console.time("异步任务-宏任务1-2");
console.time("同步任务1");
console.time("异步任务-宏任务1-1");
console.time("异步任务-微任务");
console.time("同步任务2");
console.time("同步任务3");
console.time("异步任务-宏任务2-1");
console.time("同步任务4");
console.time("异步任务-宏任务3-1");
console.time("同步任务5");
setTimeout(() => {
console.timeEnd("异步任务-宏任务1-2");
// console.log("异步任务-宏任务1-2");
}, 20);
console.timeEnd("同步任务1");
// console.log("同步任务1");
setTimeout(() => {
console.timeEnd("异步任务-宏任务1-1");
// console.log("异步任务-宏任务1-1");
queueMicrotask(() => {
console.timeEnd("异步任务-微任务");
// console.log("异步任务-微任务");
});
}, 10);
console.timeEnd("同步任务2");
// console.log("同步任务2");
for (let i = 0; i < 90000000; i++) {
// do soming
}
console.timeEnd("同步任务3");
// console.log("同步任务3");
setTimeout(() => {
console.timeEnd("异步任务-宏任务2-1");
// console.log("异步任务-宏任务2-1");
}, 2);
console.timeEnd("同步任务4");
// console.log("同步任务4");
setTimeout(() => {
console.timeEnd("异步任务-宏任务3-1");
// console.log("异步任务-宏任务3-1");
}, 3);
console.timeEnd("同步任务5");
// console.log("同步任务5");
//----------------------------------------
//同步任务1: 0.047119140625 ms
//同步任务2: 0.14501953125 ms
//同步任务3: 68.099853515625 ms
//同步任务4: 68.2099609375 ms
//同步任务5: 68.2822265625 ms
//返回 undefined -- 这里是最后一行同步代码的返回值。
//异步任务-宏任务1-1: 68.489990234375 ms
//异步任务-微任务: 68.5400390625 ms
//异步任务-宏任务1-2: 68.593994140625 ms
//异步任务-宏任务2-1: 69.541748046875 ms
//异步任务-宏任务3-1: 71.5439453125 ms
作为构造函数,只能被new执行,不能作为普通函数执行。
因为它可以被认为是基于class创建的类。
Promise() //TypeError: Promise constructor cannot be invoked without 'new' 作为构造函数,只能被NEW执行,不能作为普通函数执行「扩展:基于class创建的类,是不能当做普通函数执行的」
不兼容IE,需要导入@bable/polyfill对其进行重写,以此来兼容IE浏览器。
基于承诺者设计模式,能够有效地去管理异步编程,避免传统上基于回调函数的方式管理异步编程
所带来的回调地狱问题。
基于回调函数的方式管理异步编程
延时函数内部再执行延时函数
缺点就是回调地狱。
// 需求:编写一个delay延迟函数,执行delay函数,可以把我们要做的事件延迟一段时间执行。
// 传统方案:基于回调函数的方式,管理异步任务执行期间,要处理的事情
const delay = function delay(interval, callback) {
if (typeof interval === "function") {
// 其实传递的函数是要给callback的
callback = interval;
interval = 1000;
}
interval = +interval;
if (isNaN(interval)) interval = 1000;
if (typeof callback !== "function") callback = Function.prototype;
let timer = setTimeout(() => {
callback();
clearTimeout(timer);
}, interval);
};
// 回调地狱:回调函数中嵌套回调函数「不方便阅读和开发维护」
delay(1000,() => {
console.log("AAA");
delay(1000,() => {
console.log("BBB");
delay(1000,() => {
console.log("CCC");
});
});
});
延时函数内部再执行延时函数的并行式处理
比如预期在执行A后1000ms,再那之后经过1000ms再执行B。因为定时器定时的是1000ms,如果同步操作还经过了很长才执行,那么A与B都会进入EventQueue队列中,就会出现A执行结束后,立马就执行B的场景。
const delay = function delay(interval, callback) {
if (typeof interval === "function") {
// 其实传递的函数是要给callback的
callback = interval;
interval = 1000;
}
interval = +interval;
if (isNaN(interval)) interval = 1000;
if (typeof callback !== "function") callback = Function.prototype;
let timer = setTimeout(() => {
callback();
clearTimeout(timer);
}, interval);
};
// 回调地狱:回调函数中嵌套回调函数「不方便阅读和开发维护」
delay(1000, () => {
console.log("AAA");
});
delay(1000 + 1000, () => {
console.log("BBB");
});
delay(1000 + 1000 + 1000, () => {
console.log("CCC");
});
jQuery中提供的$.ajax方法,就是基于传统的回调函数方式,来管理异步的XMLHttpRequest请求
基于Promise方式的探索:
基于async与await的探索:
Promise得用new执行。
Promise()//会报错
// Promise() //TypeError: Promise constructor cannot be invoked without 'new' 作为构造函数,只能被NEW执行,不能作为普通函数执行「扩展:基于class创建的类,是不能当做普通函数执行的」
new执行Promise时,要求Promise中必须传递一个executor执行函数。
new Promise()//会报错,需要一个函数入参。
// new Promise() //TypeError: Promise resolver undefined is not a function NEW执行的时候,要求Promise中必须传递一个 executor 执行函数
返回一个Promise实例
const executor=()=>{}//executor
let p1 = new Promise(executor)
console.log(p1)
会把传递进来的executor回调函数并立即执行
。
而其会给executor函数传递两个实参,用于修改实例的状态。
一般会基于resolve/reject两个形参变量来接收传递的实参
实例的状态一旦被从pending改为其它状态值,则后续没有办法再修改此实例的状态和值了。
let p1 = new Promise((resolve, reject) => {
resolve(100);
reject(0);
});
console.log(p1);
状态改变之后,用then()来接收,根据状态来修改它的东西。
Function.prototype的原型是一个空匿名函数()=>{},而不是一个对象
callback=Function.prototype//Function.prototype的原型是一个空匿名函数function(){}
// 相当于
//callback=function(){}
ES6中有一个属性 new.target:
queueMicrotask是ES6的,它是创建一个异步微任务。
它内部是一个回调函数,放到队列中就会等待,必定能执行。
console.log(1111);
queueMicrotask(()=>{
console.log(2222);
})
console.log(3333);
//"1111"
//"3333"
//"2222"
(function () {
"use strict";
// 为对象设置不可枚举的属性
var define = function define(obj, key, value) {
Object.defineProperty(obj, key, {
writable: true,
configurable: true,
enumerable: false,
value: value,
});
};
// 确保是 Promise 类的实例
var ensureInstance = function ensureInstance(obj) {
if (!(obj instanceof Promise))
throw new TypeError(
"Method Promise.prototype.then called on incompatible receiver " + obj
);
};
// 基于定时器模拟 queueMicrotask 创建异步微任务「我们创建的是异步宏任务」
var queueTask = function queueTask(callback) {
if (typeof queueMicrotask !== "undefined") {
return queueMicrotask(callback);
}
var timer = setTimeout(function () {
callback();
clearTimeout(timer);
}, 0);
};
/* 构造函数 */
var Promise = function Promise(executor) {
var self = this;
if (!(self instanceof Promise))
throw new TypeError(
"Promise constructor cannot be invoked without 'new'"
);
if (typeof executor !== "function")
throw new TypeError("Promise resolver undefined is not a function");
// 为实例设置私有属性:状态和值
define(self, "state", "pending");
define(self, "result", undefined);
define(self, "onfulfilledCallback", []);
define(self, "onrejectedCallback", []);
// 修改实例状态和值
var change = function change(state, result) {
if (self.state !== "pending") return; //状态一但被更改过,则后续不能再更改了
self.state = state;
self.result = result;
// 通知集合中的方法执行「异步微任务」
queueTask(function () {
var callbacks =
self.state === "fulfilled"
? self.onfulfilledCallback
: self.onrejectedCallback;
callbacks.forEach(function (callback) {
callback(self.result);
});
});
};
// 立即执行 executor 函数
try {
executor(
function resolve(value) {
change("fulfilled", value);
},
function reject(reason) {
change("rejected", reason);
}
);
} catch (err) {
// 如果 executor 函数执行报错,也会把实例的状态改为失败,值就是报错原因
change("rejected", err);
}
};
/* 原型对象 */
var proto = Promise.prototype;
define(proto, "then", function then(onfulfilled, onrejected) {
var self = this;
ensureInstance(self);
switch (self.state) {
case "fulfilled":
queueTask(function () {
onfulfilled(self.result);
});
break;
case "rejected":
queueTask(function () {
onrejected(self.result);
});
break;
default:
self.onfulfilledCallback.push(onfulfilled);
self.onrejectedCallback.push(onrejected);
}
});
/* 暴露API「支持各种环境」 */
/* if (typeof window !== 'undefined') window.Promise = Promise
if (typeof module === 'object' && typeof module.exports === 'object') module.exports = Promise */
})();
/*
ES6中有一个属性 new.target :存储被 new 的目标
+ 普通函数执行,存储的值是 undefined
+ 构造函数执行,new.target 就是被 new 的那个构造函数
ES6中新增一个 queueMicrotask 的API,目的:创建一个可执行的异步微任务
queueMicrotask(() => {
...
})
*/