CLASS 17 · 2026-04-13 · 17-19 系统与界面

回调与图形用户界面

回调是「把函数交给别人,让别人到时候再调用」的编程手法。它驱动着异步操作、定时器和图形界面的事件处理,是理解事件循环与 Listener 模式的核心。

回调

回调(callback):客户端提供给模块的一个函数,供模块在合适的时机调用。

回调让控制流发生「反转」:不是我们主动调用库,而是库在事件发生时调用我们提供的函数。

同步回调 vs. 异步回调

// 同步回调
[1, 2, 3].map(x => x * 2);

// 异步回调:1 秒后运行
setTimeout(() => console.log("hello"), 1000);

定时器回调

setTimeout(callback, ms) 是最简单的异步回调:它把回调放入队列,至少等到指定毫秒后才执行。注意「至少」——如果事件循环正忙,实际等待可能更久。

事件循环与事件队列

TypeScript/JavaScript 用事件循环(event loop)和事件队列(event queue)来执行异步回调。主线程不断从队列中取出任务执行;当任务发起异步操作时,它的回调被排入队列,等待下一轮处理。

事件循环:不断从事件队列取出回调并执行的机制,保证了单线程下异步任务的有序调度。

用 Promise 组合回调

Promise<T>.then(callback) 把一个回调附加到 Promise 上,并在 Promise 完成时调用它,同时返回一个新的 Promise。这是把回调风格与 Promise 风格桥接起来的关键。

fetch("/api/user")
  .then(res => res.json())        // 回调 1:解析 JSON
  .then(user => user.name)        // 回调 2:取出名字
  .then(name => console.log(name)); // 回调 3:打印

拆解 async 函数

await 并不是魔法——它的本质是:把 await 之后的剩余计算包装成一个 then 回调,附加到被等待的 Promise 上。理解这一点,就能在回调与 async/await 之间自由切换。

// 这两种写法在语义上等价:
async function f() {
  const x = await p;
  console.log(x);
}

function f() {
  return p.then(x => console.log(x));
}

HTML 与 DOM

HTML 元素构成一棵树,称为文档对象模型(Document Object Model, DOM)。JavaScript 可以读取和修改这棵树,从而动态改变页面内容和结构。

输入处理:Listener 模式

GUI 事件处理采用监听者模式(Listener pattern):

const button = document.querySelector("#submit");
button.addEventListener("click", (event) => {
  console.log("按钮被点击了");
});

监听者的常见陷阱

关键要点

  • 回调客户端提供给模块、由模块在适当时机调用的函数。
  • 同步/异步同步回调立即执行,异步回调排入事件队列。
  • 事件循环单线程下通过事件队列有序调度异步任务。
  • thenPromise.then 把回调附加到完成时,返回新 Promise。
  • 拆解 awaitawait 等价于把剩余计算包装成 then 回调。
  • ListenerGUI 用监听者模式处理事件,注意快执行、防循环、做清理。