异步编程
题目1: 什么是回调函数?如何避免回调地狱?
答案: 回调函数是作为参数传递给另一个函数的函数,它在某个操作完成后被调用。回调地狱是指多层嵌套的回调函数,使得代码难以阅读和维护。
避免回调地狱的方法:
- 使用 Promise
- 使用 async/await
- 模块化和函数命名
- 使用控制流程库(如 async.js)
扩展:
javascript
// 回调地狱示例
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
getMoreData(c, function(d) {
getMoreData(d, function(e) {
// ...
});
});
});
});
});
// 使用 Promise 改写
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.then(c => getMoreData(c))
.then(d => getMoreData(d))
.then(e => {
// ...
})
.catch(error => {
// 错误处理
});
题目2: Promise 的基本用法和状态有哪些?
答案: Promise 是异步编程的一种解决方案,比传统的回调函数更加优雅。
Promise 的基本用法:
javascript
const promise = new Promise((resolve, reject) => {
// 异步操作
if (/* 操作成功 */) {
resolve(value);
} else {
reject(error);
}
});
promise.then(value => {
// 成功的回调
}).catch(error => {
// 失败的回调
});
Promise 有三种状态:
- Pending(进行中)
- Fulfilled(已成功)
- Rejected(已失败)
扩展: Promise 的状态一旦改变,就不会再变。这也就是 Promise 名字的由来,它的英语意思是"承诺",表示其状态的不可逆性。
题目3: async/await 的工作原理是什么?
答案: async/await 是建立在 Promise 之上的语法糖,让异步代码看起来像同步代码。
async
函数返回一个 Promise 对象await
关键字只能在async
函数内部使用await
会暂停函数的执行,等待 Promise 解决
扩展:
javascript
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
}
题目4: 如何处理异步错误?
答案: 处理异步错误的主要方法有:
- 使用 Promise 的
.catch()
方法 - 在 async/await 中使用 try/catch 块
- 使用
Promise.all()
的错误处理 - 全局的未捕获 Promise 错误处理
扩展:
javascript
// Promise 错误处理
promise
.then(result => {
// 处理结果
})
.catch(error => {
console.error('Error:', error);
});
// async/await 错误处理
async function fetchData() {
try {
const data = await getData();
// 处理数据
} catch (error) {
console.error('Error:', error);
}
}
// 全局未捕获 Promise 错误处理
window.addEventListener('unhandledrejection', event => {
console.error('Unhandled rejection:', event.reason);
});
题目5: 什么是 Promise.all() 和 Promise.race()?
答案:
Promise.all()
接收一个 Promise 数组作为输入,当所有 Promise 都成功时返回一个包含所有结果的数组,如果任何一个 Promise 失败,则立即返回失败的结果。Promise.race()
同样接收一个 Promise 数组,但只要有一个 Promise 完成(无论成功或失败),就立即返回那个 Promise 的结果。
扩展:
javascript
// Promise.all() 示例
const promises = [fetch('/api/data1'), fetch('/api/data2'), fetch('/api/data3')];
Promise.all(promises)
.then(results => {
// 处理所有结果
})
.catch(error => {
// 处理任何错误
});
// Promise.race() 示例
const promise1 = new Promise(resolve => setTimeout(() => resolve('one'), 1000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('two'), 2000));
Promise.race([promise1, promise2])
.then(result => console.log(result)); // 输出: "one"
题目6: 如何实现一个简单的 Promise?
答案: 实现一个简单的 Promise 需要考虑状态管理、异步操作、then 方法链式调用等。以下是一个基本实现:
javascript
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(cb => cb(value));
}
};
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(cb => cb(reason));
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
return new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolve(x);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolve(x);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolve(x);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolve(x);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
}
}
扩展: 这个实现包含了 Promise 的基本功能,但还缺少 catch、finally 等方法,以及更复杂的错误处理和 Promise 解析过程。完整的 Promise 实现需要遵循 Promises/A+ 规范。
题目7: 什么是 Generator 函数?它与异步编程有什么关系?
答案: Generator 函数是 ES6 引入的一种函数类型,可以被暂停和恢复执行。它们通过 yield
关键字来定义暂停点。
Generator 函数与异步编程的关系:
- 可以用于控制异步操作的流程
- 可以将异步操作变成同步的写法
- 是 async/await 的前身和基础
扩展:
javascript
function* asyncFlow() {
const result1 = yield fetchData1();
console.log(result1);
const result2 = yield fetchData2(result1);
console.log(result2);
}
function run(generator) {
const iterator = generator();
function handle(yielded) {
if (!yielded.done) {
yielded.value.then(result => handle(iterator.next(result)));
}
}
handle(iterator.next());
}
run(asyncFlow);
题目8: 什么是事件循环(Event Loop)?
答案: 事件循环是 JavaScript 的执行模型,用于管理异步操作。它是一个持续运行的循环,不断地从任务队列中取出任务并执行。
事件循环的主要组成部分:
- 任务队列(Task Queue):存储待执行的任务
- 事件循环(Event Loop):不断地从任务队列中取出任务并执行
- 微任务队列(Microtask Queue):存储微任务,优先级高于宏任务
题目9: 什么是宏任务(Macro-task)和微任务(Micro-task)?
答案: 宏任务(Macro-task)和微任务(Micro-task)是事件循环中的两种任务类型。
宏任务:
setTimeout
setInterval
setImmediate
requestAnimationFrame
微任务:
Promise
MutationObserver
process.nextTick
(Node.js)
扩展: 在事件循环的每个周期中,宏任务和微任务的执行顺序是有规定的:
- 执行所有微任务
- 执行一个宏任务
- 重复上述过程,直到所有任务执行完毕
扩展:
javascript
// 示例代码
console.log('start');
setTimeout(() => {
console.log('setTimeout');
});
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('end');
// 输出顺序:start -> end -> Promise -> setTimeout