1. asynchronousprogrammingoverview
in JavaScript in, asynchronousprogramming is aprocessing非阻塞operation programming范式. 由于 JavaScript is 单thread , asynchronousprogramming允许程序 in etc.待某些operationcompletion (such asnetworkrequest, file读写, 定时器etc.) 时继续执行othercode, 而不 is 阻塞整个程序 执行.
common asynchronousoperationincluding:
networkrequest (AJAX, Fetch API)
file读写operation
定时器 (setTimeout, setInterval)
eventprocessing
Promise and async/await
2. synchronization vs asynchronous
for 了更 good 地understandingasynchronousprogramming, 我们先来比较synchronization and asynchronouscode 执行方式:
2.1 synchronizationcode
synchronizationcode按照code 顺序依次执行, before 一个operationcompletion after 才会执行 under 一个operation.
console.log('开始执行');
// synchronizationoperation: 立即执行
function synchronousOperation() {
console.log('synchronizationoperation执行in...');
// mock耗时operation
for (let i = 0; i < 1000000000; i++) {
// 空循环, mock耗时
}
console.log('synchronizationoperationcompletion');
}
synchronousOperation();
console.log('继续执行othercode');
// 输出顺序:
// 开始执行
// synchronizationoperation执行in...
// synchronizationoperationcompletion
// 继续执行othercode
2.2 asynchronouscode
asynchronouscode不会阻塞程序 执行, 而 is in after 台执行, 当operationcompletion after throughcallbackfunction, Promise or async/await 来processing结果.
console.log('开始执行');
// asynchronousoperation: 不会阻塞 after 续code执行
function asynchronousOperation() {
console.log('asynchronousoperation开始');
setTimeout(function() {
console.log('asynchronousoperationcompletion');
}, 2000);
}
asynchronousOperation();
console.log('继续执行othercode');
// 输出顺序:
// 开始执行
// asynchronousoperation开始
// 继续执行othercode
// (2秒 after ) asynchronousoperationcompletion
3. callbackfunction
callbackfunction is 最basic asynchronousprogramming模式, 它 is a serving asparameter传递给另一个function function, 当asynchronousoperationcompletion时被调用.
3.1 basiccallback
// mockasynchronousoperation
function fetchData(callback) {
console.log('开始获取data...');
setTimeout(function() {
const data = { name: '张三', age: 25 };
console.log('data获取completion');
callback(data); // 调用callbackfunction, 传递data
}, 2000);
}
// 调用asynchronousfunction, 传入callback
fetchData(function(data) {
console.log('processing获取 to data:', data);
console.log('dataprocessingcompletion');
});
console.log('继续执行othercode...');
// 输出顺序:
// 开始获取data...
// 继续执行othercode...
// (2秒 after ) data获取completion
// processing获取 to data: { name: '张三', age: 25 }
// dataprocessingcompletion
3.2 callback地狱
当 many 个asynchronousoperation需要按顺序执行时, callbackfunction会嵌套 in 一起, 形成所谓 "callback地狱" (Callback Hell) , 使code难以阅读 and maintenance.
// callback地狱example
function getUser(userId, callback) {
setTimeout(function() {
console.log('获取userinformation');
callback({ id: userId, name: '张三' });
}, 1000);
}
function getPosts(user, callback) {
setTimeout(function() {
console.log('获取user帖子');
callback([{ id: 1, title: '帖子1' }, { id: 2, title: '帖子2' }]);
}, 1000);
}
function getComments(posts, callback) {
setTimeout(function() {
console.log('获取帖子评论');
callback([{ id: 1, content: '评论1' }, { id: 2, content: '评论2' }]);
}, 1000);
}
// 嵌套 callbackfunction - callback地狱
getUser(1, function(user) {
getPosts(user, function(posts) {
getComments(posts, function(comments) {
console.log('最终结果:', {
user: user,
posts: posts,
comments: comments
});
});
});
});
// 输出顺序:
// 获取userinformation
// 获取user帖子
// 获取帖子评论
// 最终结果: { ... }
4. Promise
Promise is ES6 引入 一种processingasynchronousoperation object, 它代表一个asynchronousoperation 最终completion ( or 失败) 及其结果值. Promise 可以解决callback地狱 issues, 使asynchronouscode更加清晰 and 易于maintenance.
4.1 Promise basic用法
// creation Promise
const promise = new Promise(function(resolve, reject) {
// asynchronousoperation
setTimeout(function() {
const success = true;
if (success) {
resolve('operation成功!'); // 成功时调用 resolve
} else {
reject('operation失败!'); // 失败时调用 reject
}
}, 2000);
});
// using Promise
promise
.then(function(result) {
console.log('成功:', result);
return 'processing after 结果';
})
.then(function(processedResult) {
console.log('processing:', processedResult);
})
.catch(function(error) {
console.log('error:', error);
})
.finally(function() {
console.log('operationcompletion, 无论成功 or 失败都会执行');
});
console.log('Promise creationcompletion, etc.待结果...');
// 输出顺序:
// Promise creationcompletion, etc.待结果...
// (2秒 after ) 成功: operation成功!
// processing: processing after 结果
// operationcompletion, 无论成功 or 失败都会执行
4.2 Promise 链式调用
Promise 一个 important features is 可以链式调用, 每个 then method都返回一个 new Promise, 这使得我们可以按顺序执行 many 个asynchronousoperation, 而不需要嵌套callback.
// using Promise 重写之 before callback地狱example
function getUser(userId) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log('获取userinformation');
resolve({ id: userId, name: '张三' });
}, 1000);
});
}
function getPosts(user) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log('获取user帖子');
resolve([{ id: 1, title: '帖子1' }, { id: 2, title: '帖子2' }]);
}, 1000);
});
}
function getComments(posts) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log('获取帖子评论');
resolve([{ id: 1, content: '评论1' }, { id: 2, content: '评论2' }]);
}, 1000);
});
}
// using Promise 链式调用, 避免callback地狱
getUser(1)
.then(function(user) {
return getPosts(user).then(function(posts) {
return { user: user, posts: posts };
});
})
.then(function(data) {
return getComments(data.posts).then(function(comments) {
return {
user: data.user,
posts: data.posts,
comments: comments
};
});
})
.then(function(result) {
console.log('最终结果:', result);
})
.catch(function(error) {
console.log('error:', error);
});
// 更简洁 链式调用写法
getUser(1)
.then(user => getPosts(user).then(posts => ({ user, posts })))
.then(({ user, posts }) => getComments(posts).then(comments => ({ user, posts, comments })))
.then(result => console.log('最终结果:', result))
.catch(error => console.log('error:', error));
4.3 Promise 静态method
// Promise.all - etc.待所 has Promise completion
const promise1 = new Promise(resolve => setTimeout(() => resolve('结果1'), 1000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('结果2'), 2000));
const promise3 = new Promise(resolve => setTimeout(() => resolve('结果3'), 1500));
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log('所 has Promise completion:', results);
// 输出: 所 has Promise completion: [ '结果1', '结果2', '结果3' ]
})
.catch(error => {
console.log(' has Promise 失败:', error);
});
// Promise.race - etc.待第一个completion Promise
Promise.race([promise1, promise2, promise3])
.then(result => {
console.log('第一个completion Promise 结果:', result);
// 输出: 第一个completion Promise 结果: 结果1
})
.catch(error => {
console.log(' has Promise 失败:', error);
});
// Promise.resolve - creation一个已解决 Promise
Promise.resolve('直接结果')
.then(result => {
console.log('Promise.resolve 结果:', result);
// 输出: Promise.resolve 结果: 直接结果
});
// Promise.reject - creation一个已拒绝 Promise
Promise.reject('直接error')
.catch(error => {
console.log('Promise.reject error:', error);
// 输出: Promise.reject error: 直接error
});
5. async/await
async/await is ES2017 引入 语法糖, 它基于 Promise, 使asynchronouscode看起来更像synchronizationcode, 进一步improving了code readable 性 and 可maintenance性.
5.1 basic用法
// 定义asynchronousfunction
async function fetchData() {
console.log('开始获取data...');
// using await etc.待 Promise completion
const result = await new Promise(function(resolve) {
setTimeout(function() {
resolve('data获取成功!');
}, 2000);
});
console.log('获取 to data:', result);
return 'processing after 结果';
}
// 调用asynchronousfunction
fetchData()
.then(function(result) {
console.log('最终结果:', result);
})
.catch(function(error) {
console.log('error:', error);
});
console.log('asynchronousfunction调用completion, etc.待结果...');
// 输出顺序:
// 开始获取data...
// asynchronousfunction调用completion, etc.待结果...
// (2秒 after ) 获取 to data: data获取成功!
// 最终结果: processing after 结果
5.2 errorprocessing
// asynchronousfunctionin errorprocessing
async function fetchData() {
try {
console.log('开始获取data...');
// mock成功 and 失败 circumstances
const success = false;
const result = await new Promise(function(resolve, reject) {
setTimeout(function() {
if (success) {
resolve('data获取成功!');
} else {
reject('data获取失败!');
}
}, 2000);
});
console.log('获取 to data:', result);
return 'processing after 结果';
} catch (error) {
console.log('捕获 to error:', error);
throw error; // 重 new 抛出error, 让调用者知道
} finally {
console.log('operationcompletion, 无论成功 or 失败都会执行');
}
}
// 调用asynchronousfunction
fetchData()
.then(function(result) {
console.log('最终结果:', result);
})
.catch(function(error) {
console.log('调用者捕获 to error:', error);
});
// 输出顺序:
// 开始获取data...
// (2秒 after ) 捕获 to error: data获取失败!
// operationcompletion, 无论成功 or 失败都会执行
// 调用者捕获 to error: data获取失败!
5.3 asynchronousfunction 链式调用
// 重写之 before example, using async/await
function getUser(userId) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log('获取userinformation');
resolve({ id: userId, name: '张三' });
}, 1000);
});
}
function getPosts(user) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log('获取user帖子');
resolve([{ id: 1, title: '帖子1' }, { id: 2, title: '帖子2' }]);
}, 1000);
});
}
function getComments(posts) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log('获取帖子评论');
resolve([{ id: 1, content: '评论1' }, { id: 2, content: '评论2' }]);
}, 1000);
});
}
// using async/await 重写, code更加清晰
async function fetchUserData(userId) {
try {
const user = await getUser(userId);
const posts = await getPosts(user);
const comments = await getComments(posts);
return {
user: user,
posts: posts,
comments: comments
};
} catch (error) {
console.log('error:', error);
throw error;
}
}
// 调用asynchronousfunction
fetchUserData(1)
.then(function(result) {
console.log('最终结果:', result);
})
.catch(function(error) {
console.log('调用者捕获 to error:', error);
});
// 输出顺序:
// 获取userinformation
// 获取user帖子
// 获取帖子评论
// 最终结果: { user: { id: 1, name: '张三' }, posts: [...], comments: [...] }
6. asynchronousprogramming模式
6.1 parallel执行
当 many 个asynchronousoperation之间没 has 依赖relationships时, 可以parallel执行它们以improvingperformance.
// parallel执行 many 个asynchronousoperation
async function fetchDataInParallel() {
console.log('开始parallel获取data...');
// 同时开始所 has asynchronousoperation
const promise1 = new Promise(resolve => setTimeout(() => resolve('data1'), 2000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('data2'), 1500));
const promise3 = new Promise(resolve => setTimeout(() => resolve('data3'), 1000));
// etc.待所 has operationcompletion
const [result1, result2, result3] = await Promise.all([promise1, promise2, promise3]);
console.log('所 has data获取completion:');
console.log('结果1:', result1);
console.log('结果2:', result2);
console.log('结果3:', result3);
return { result1, result2, result3 };
}
fetchDataInParallel()
.then(result => console.log('最终结果:', result))
.catch(error => console.log('error:', error));
// 输出顺序:
// 开始parallel获取data...
// (约2秒 after ) 所 has data获取completion:
// 结果1: data1
// 结果2: data2
// 结果3: data3
// 最终结果: { result1: 'data1', result2: 'data2', result3: 'data3' }
6.2 串行执行
当asynchronousoperation之间 has 依赖relationships时, 需要串行执行它们.
// 串行执行 many 个asynchronousoperation
async function fetchDataInSequence() {
console.log('开始串行获取data...');
// 按顺序执行asynchronousoperation
const result1 = await new Promise(resolve => {
setTimeout(() => {
console.log('获取data1completion');
resolve('data1');
}, 1000);
});
const result2 = await new Promise(resolve => {
setTimeout(() => {
console.log('获取data2completion');
resolve('data2');
}, 1000);
});
const result3 = await new Promise(resolve => {
setTimeout(() => {
console.log('获取data3completion');
resolve('data3');
}, 1000);
});
console.log('所 has data获取completion:');
console.log('结果1:', result1);
console.log('结果2:', result2);
console.log('结果3:', result3);
return { result1, result2, result3 };
}
fetchDataInSequence()
.then(result => console.log('最终结果:', result))
.catch(error => console.log('error:', error));
// 输出顺序:
// 开始串行获取data...
// (1秒 after ) 获取data1completion
// (再1秒 after ) 获取data2completion
// (再1秒 after ) 获取data3completion
// 所 has data获取completion:
// 结果1: data1
// 结果2: data2
// 结果3: data3
// 最终结果: { result1: 'data1', result2: 'data2', result3: 'data3' }
7. Fetch API
Fetch API is 现代浏览器providing 用于fornetworkrequest API, 它基于 Promise, providing了更简洁, 更强 big 方式来for HTTP request.
7.1 basic用法
// using Fetch API 获取data
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('networkresponse失败: ' + response.status);
}
return response.json(); // 解析 JSON response
})
.then(data => {
console.log('获取 to data:', data);
})
.catch(error => {
console.log('error:', error);
});
// using async/await 语法
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('networkresponse失败: ' + response.status);
}
const data = await response.json();
console.log('获取 to data:', data);
return data;
} catch (error) {
console.log('error:', error);
throw error;
}
}
fetchData()
.then(data => console.log('processingdata:', data))
.catch(error => console.log('调用者捕获error:', error));
7.2 发送 POST request
// using Fetch API 发送 POST request
async function sendData() {
try {
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: '张三', age: 25 })
});
if (!response.ok) {
throw new Error('networkresponse失败: ' + response.status);
}
const data = await response.json();
console.log('server返回 data:', data);
return data;
} catch (error) {
console.log('error:', error);
throw error;
}
}
sendData()
.then(data => console.log('request成功:', data))
.catch(error => console.log('request失败:', error));
8. asynchronousprogrammingoptimization
asynchronousprogramming虽然强 big , 但也需要注意一些optimizationtechniques, 以improvingcode performance and 可maintenance性.
8.1 避免过度using await
// 不推荐: 不必要 await 导致串行执行
async function fetchData() {
const result1 = await fetch('https://api.example.com/data1');
const data1 = await result1.json();
const result2 = await fetch('https://api.example.com/data2');
const data2 = await result2.json();
const result3 = await fetch('https://api.example.com/data3');
const data3 = await result3.json();
return { data1, data2, data3 };
}
// 推荐: parallel执行独立 request
async function fetchDataoptimizationd() {
// 同时开始所 has request
const promise1 = fetch('https://api.example.com/data1').then(res => res.json());
const promise2 = fetch('https://api.example.com/data2').then(res => res.json());
const promise3 = fetch('https://api.example.com/data3').then(res => res.json());
// etc.待所 has requestcompletion
const [data1, data2, data3] = await Promise.all([promise1, promise2, promise3]);
return { data1, data2, data3 };
}
8.2 using Promise.allSettled
当你需要etc.待所 has Promise completion, 无论它们成功还 is 失败时, 可以using Promise.allSettled.
// using Promise.allSettled
const promise1 = Promise.resolve('成功1');
const promise2 = Promise.reject('失败2');
const promise3 = Promise.resolve('成功3');
Promise.allSettled([promise1, promise2, promise3])
.then(results => {
console.log('所 has Promise status:', results);
// filter出成功 结果
const successfulResults = results
.filter(result => result.status === 'fulfilled')
.map(result => result.value);
console.log('成功 结果:', successfulResults);
// filter出失败 结果
const failedResults = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
console.log('失败 结果:', failedResults);
});
// 输出:
// 所 has Promise status: [
// { status: 'fulfilled', value: '成功1' },
// { status: 'rejected', reason: '失败2' },
// { status: 'fulfilled', value: '成功3' }
// ]
// 成功 结果: [ '成功1', '成功3' ]
// 失败 结果: [ '失败2' ]
实践case: asynchronousdataprocessing
fake设我们需要 from many 个 API 端点获取data, 并forprocessing after 展示给user. 我们将using async/await and Promise.all 来optimization这个过程.
// mock API request
function fetchUsers() {
return new Promise(resolve => {
setTimeout(() => {
console.log('获取userdata');
resolve([
{ id: 1, name: '张三', posts: 5 },
{ id: 2, name: '李四', posts: 3 },
{ id: 3, name: '王五', posts: 8 }
]);
}, 1000);
});
}
function fetchPosts(userId) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`获取user ${userId} 帖子`);
resolve([
{ id: 1, userId: userId, title: `帖子 ${userId}-1` },
{ id: 2, userId: userId, title: `帖子 ${userId}-2` }
]);
}, 500);
});
}
function fetchComments(postId) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`获取帖子 ${postId} 评论`);
resolve([
{ id: 1, postId: postId, content: `评论 ${postId}-1` },
{ id: 2, postId: postId, content: `评论 ${postId}-2` }
]);
}, 300);
});
}
// 主function: 获取所 has data并processing
async function fetchAllData() {
try {
console.log('开始获取data...');
// 1. 获取所 has user
const users = await fetchUsers();
console.log('userdata获取completion:', users);
// 2. parallel获取所 has user 帖子
const userPostsPromises = users.map(user => fetchPosts(user.id));
const userPosts = await Promise.all(userPostsPromises);
console.log('帖子data获取completion:', userPosts);
// 3. parallel获取所 has 帖子 评论
const allPosts = userPosts.flat(); // merge所 has 帖子
const commentsPromises = allPosts.map(post => fetchComments(post.id));
const comments = await Promise.all(commentsPromises);
console.log('评论data获取completion:', comments);
// 4. processingdata, 构建完整 userinformation
const result = users.map((user, index) => ({
...user,
posts: userPosts[index].map(post => {
const postComments = comments.find(commentList =>
commentList.some(comment => comment.postId === post.id)
) || [];
return {
...post,
comments: postComments
};
})
}));
console.log('dataprocessingcompletion, 最终结果:');
console.log(JSON.stringify(result, null, 2));
return result;
} catch (error) {
console.log('error:', error);
throw error;
} finally {
console.log('所 has operationcompletion');
}
}
// 执行主function
fetchAllData()
.then(result => console.log('执行成功, 返回结果:', result.length, '个user'))
.catch(error => console.log('执行失败:', error));
互动练习: asynchronousoperationmock
请completion以 under asynchronousoperationtask:
creation一个mock asynchronousoperationfunction, mocknetworkrequest
implementationparallel执行 many 个asynchronousoperation
implementation串行执行 many 个asynchronousoperation
比较两种执行方式 performancediff
asynchronousoperationmock器
parallel执行
串行执行