setTimeout
setTimeout的核心作用流程
1. 主线程移交任务
- 主线程执行到
setTimeout(callback, delay)
时,立即将callback
函数和delay
延迟时间交给浏览器的定时器模块(Web API),主线程继续执行后续同步代码(不阻塞)。
2. 定时器倒计时
- 浏览器的定时器线程开始计时,独立于主线程运行,等待指定的
delay
时间(如 1000ms)。
3. 回调进入任务队列
- 当倒计时结束,定时器线程将
callback
函数推入宏任务队列(Task Queue),此时回调函数仍未执行,只是进入排队状态。
4. 事件循环调度执行
- 主线程的同步代码全部执行完毕后(调用栈为空),事件循环(Event Loop)检查任务队列:
- 优先清空微任务队列(如 Promise 回调)。
- 从宏任务队列中取出
callback
,推入调用栈,由主线程执行。
事件驱动
除了setTimeout,Javascript还可以通过事件驱动来控制异步代码的执行,基于几个基本概念:
- 事件循环(Event Loop): JavaScript 运行时的核心机制,不断检查是否有待处理的事件
- 事件队列(Event Queue): 存储待处理的事件和回调函数
- 事件发射器(Event Emitter): 产生事件的对象
const button = document.getElementById('myButton');
// 按钮作为事件发射器Event Emitter,我们给它添加监听器
button.addEventListener('click', function(event) {
console.log('按钮被点击了!', event);
});
发布—订阅设计模式
发布—订阅并不是Javascript中的原生概念,而是一种设计模式,又称观察者模式。
运行机制:创建一个“信号中心”,某个任务执行完成,就向信号中心“发布”(publish)一个信号,其他任务可以向信号中心“订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。
// 极简发布-订阅实现
class EventEmitter {
constructor() {
this.events = {};
}
// 订阅事件
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
// 发布事件
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(callback => callback(...args));
}
}
// 取消订阅
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
}
}
// 使用示例
const emitter = new EventEmitter();
// 订阅者1
const subscriber1 = (data) => {
console.log('Subscriber1 received:', data);
};
// 订阅者2
const subscriber2 = (data) => {
console.log('Subscriber2 received:', data);
};
// 订阅事件
emitter.on('message', subscriber1);
emitter.on('message', subscriber2);
// 发布事件
emitter.emit('message', 'Hello World!');
// 输出:
// Subscriber1 received: Hello World!
// Subscriber2 received: Hello World!
// 取消订阅
emitter.off('message', subscriber1);
// 再次发布
emitter.emit('message', 'Goodbye!');
// 输出:
// Subscriber2 received: Goodbye!
Promise
当多个异步操作需要顺序执行时,传统的回调写法会形成层层嵌套,导致难以阅读和维护。Promise为了解决Javascript中的“回调地狱”问题应运而生。
// 传统写法
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);
Promise的状态转换
pending(等待中)
/ \
fulfilled rejected
(成功) (失败)
Promise的创建模板
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己实现。
const promise = new Promise((resolve, reject) => {
// 这里是你的异步操作
if (/* 操作成功 */) {
resolve('成功的结果'); // 触发.then()
} else {
reject('失败的原因'); // 触发.catch()
}
});
Promise极简示例
// 创建Promise
const weatherPromise = new Promise((resolve, reject) => {
const isSunny = Math.random() > 0.5;
setTimeout(() => {
isSunny ? resolve('晴天') : reject('下雨');
}, 1000);
});
// 使用Promise
weatherPromise
.then(weather => console.log(`今天是${weather},去野餐!`))
.catch(reason => console.log(`因为${reason},取消野餐`));
Promise“升级版”——async和await
async/await 是 JavaScript 处理异步操作的语法糖,它是基于 Promise 的,但让异步代码写起来更像同步代码,更易读和维护。
async关键字标记一个函数为“异步函数”,特性:
- 自动将返回值包装成 Promise
- 函数内可以使用 await
// Promise写法
function getUser() {
fetch('/user')
.then(response => response.json())
.then(user => console.log(user))
.catch(error => console.error(error));
}
// async/await写法
async function getUser() {
try {
const response = await fetch('/user');
const user = await response.json();
console.log(user);
} catch (error) {
console.error(error);
}
}