Iterator 迭代器
在 JavaScript 中,数组、字符串、Map、Set 等都是可迭代对象。但如果你想要遍历一个自定义的数据结构(比如链表、树),没有内置遍历机制该怎么办?
这就是 迭代器模式 出现的原因 ------ 提供统一的访问接口,让不同的数据结构都可以被 for...of
遍历。
工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用next()方法,指针自动指向数据结构的第一个成员
- 每次调用next(),指针就往后移动
- 每次next(),返回一个对象{value,done}
- value:当前成员值
- done:是否遍历完成
js
const arr = [10, 20, 30];
const it = arr[Symbol.iterator]();
console.log(it.next()); // { value: 10, done: false }
console.log(it.next()); // { value: 20, done: false }
console.log(it.next()); // { value: 30, done: false }
console.log(it.next()); // { value: undefined, done: true }
本质
- 把遍历逻辑抽象成一个接口
- 内部维护当前位置,每次调用next()更新状态
手写迭代器
js
function createIterator(arr) {
let index = 0;
return {
next() {
if (index < arr.length) {
return { value: arr[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
const it = createIterator([1, 2, 3]);
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }
Generator 生成器
Generator 是 特殊的函数 ,用 function*
定义,执行时可以被"暂停"和"恢复"。
工作原理
- 调用
function*
定义的函数,不会立即执行,而是返回一个迭代器对象 - 调用next(),函数执行到yeild暂停,并返回对应的值
- 下次执行next(),会接着上次暂停处继续
- 可以通过next(value)把值传回函数内部
- 直到函数结束,返回{value:undefined,done:true}
js
function* gen() {
yield 1;
yield 2;
yield 3;
}
const it = gen();
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }
本质
- 是迭代协议的语法糖
- 内部是一个状态机,能在不同状态间来回切换
- 支持协程风格的异步编程(后来async/await取代了它)
Promise
为什么需要Promise?
- 解决回调地狱
- 提供统一的异步 API (
then/catch/finally
) - 可链式调用,错误统一处理
工作原理
- 创建时立即执行
executor(resolve, reject)
- 内部维护三种状态
- pending
- fulfilled
- rejected
- 状态发生变化就不可逆
- 调用then/catch方法,回调会放入微任务队列,在事件循环末端执行
js
const p = new Promise((resolve, reject) => {
setTimeout(() => resolve("ok"), 1000);
});
p.then(res => console.log(res)); // 1秒后打印 "ok"
本质
-
状态机 + 异步任务调度器;
-
把异步结果抽象成一个"可组合的值";
-
避免了传统回调地狱。
async/await
为什么需要async/await?
- Promise 链虽然解决了回调地狱,但可读性仍差
- async/await 可以写出 同步风格异步代码
- 错误处理更简单(try/catch)
工作原理
- async function调用后,始终返回一个Promise
- 执行遇到await时
- 表达式立即执行
- 如果是普通值,转成
Promise.resolve(value)
- 如果是promise,暂停函数执行,等待结果
- 等 Promise 完成后,恢复执行,并把结果返回给
await
表达式 - 如果Promise失败,抛出错误,可以使用try...catch捕获
js
async function getData() {
const res = await new Promise(resolve => setTimeout(() => resolve("ok"), 1000));
console.log(res); // "ok"
}
getData();
本质
- Generator + Promise 的语法糖
- Babel 转译 async/await → Generator
- 让异步代码看起来像同步,更易读易维护
babel转译原理
js
async function fetchData() {
const a = await getA();
const b = await getB(a);
return b;
}
// 转译等价于
function fetchData() {
return _asyncToGenerator(function* () {
const a = yield getA();
const b = yield getB(a);
return b;
})();
}
function _asyncToGenerator(genFn) {
return function() {
const gen = genFn.apply(this, arguments);
return new Promise((resolve, reject) => {
function step(key, arg) {
let info;
try { info = gen[key](arg); }
catch (err) { return reject(err); }
if (info.done) resolve(info.value);
else Promise.resolve(info.value)
.then(val => step("next", val), err => step("throw", err));
}
step("next");
});
}
}