我对于这一部分知识起初也是模棱两可的,经过我大量搜索👨💻👨💻👨💻👨💻👨💻,现在算是搞明白了。
👨🎓👨🎓👨🎓👨🎓 在这里给大家说一下,我目前是一名大学生,哪里不对,敬请指点。
下面我们一起来探讨吧。🙋♂️🙋♂️🙋♂️🙋♂️🙋♂️🙋♂️🙋♂️🙋♂️🙋♂️
📢📢📢:首先需要搞明白什么是同步、异步、推迟
🌟同步、异步、推迟含义
同步
从图中可以看出,同步就像是一条路,但这条路只能容下一个人走,因此前面的人不走快一点,后面的人想走快也没有办法。
所以同步的特点
- 代码自上而下执行,上一句执行完,才能执行下一句;
- 即使中间有循环,定时器等,也要等它执行完,才能继续往下执行。
异步(async)
为了解决同步执行效率低的问题,引出了异步编程。
从图中可以看出,异步就像是两条路,两个人互不影响,后面的人想走到前面,也是可以的。
再比如我们做饭:
- 洗菜
- 切菜
- 煲米
- 炒菜
这四个步骤:我们总不能在煲米的时候,一直等它熟,然后再去炒菜。煲米的时候,我们也可以执行炒菜,所以这就是异步。
异步的特点
- 代码可以不用自上而下上一行执行完毕,才能执行下一行;
- 遇到需要等待的操作,同时往下执行不需要等待的操作,我们可以把需要等待的操作(比如循环,定时器,网络请求等)放入队列中,等待其它不需要等待的操作执行完毕,再依次执行它们。
推迟(defer)
再以异步做饭操作为例,假如我们先把菜炒好了,但是大米还没有煲熟,那么我们还需要等待大米熟了之后才能干饭。这个时候就需要推迟。
从上面图中可以看出,两个人走两条路,其中一个人先到了出口之后,必须等待另一个人先通过之后,自己才能通过。
所以推迟的特点
代码可以同时编译,但是某一行代码执行,必须依赖上一行代码执行完毕才能执行
异步和推迟在加载JS文档和渲染文档中的应用
defer 和 async 都是用于异步加载脚本的属性,它们的主要区别在于脚本加载和执行的时间点不同。具体来说:
- defer(推迟): 在 HTML5 中被标准化,它的作用是告诉浏览器立即下载脚本文件,但是推迟脚本的执行,等到文档解析完成后再执行脚本,但是并不能保证多个 defer 脚本的执行顺序。
<head> <script defer src="script.js"></script> </head>
- async(异步): 也是 HTML5 中的一个标准属性,它的作用是告诉浏览器立即下载脚本文件,并且异步执行该脚本,不会阻塞页面其他内容的加载。当多个 async 的脚本同时存在时,它们的执行顺序是无法确定的。
<head> <script async src="script.js"></script> </head>
总的来说,defer 用于异步加载脚本并推迟脚本的执行,而 async 用于异步加载脚本并立即执行脚本。
那么我们也知道了异步是有一定的弊端的,就比如 做饭==>吃饭 吃饭是肯定在做饭后面,但是加入异步操作后,可能先执行吃饭,再执行做饭。唉!这是不是有点搞笑!!!😅😅😅😅😅😅
那么我们就需要异步的解决方案来解决这个问题
🌟异步编程解决方案
回调函数(Callbacks)
在异步操作完成后,通过传递一个回调函数来处理返回结果。这是最早也是最基础的异步编程模式。
function fetchData(callback) {
setTimeout(() => {
const data = 'Hello, World!';
callback(data);
}, 1000);
}
fetchData((data) => {
console.log(data); // 输出:Hello, World!
});
Promise
Promise 是一种用于处理异步操作的对象,它可以表示一个异步操作的最终完成或失败,并提供了链式调用的语法来处理操作结果。
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = 'Hello, World!';
resolve(data);
}, 1000);
});
}
fetchData().then((data) => {
console.log(data); // 输出:Hello, World!
});
async/await
async/await 是 ES2017 引入的语法糖,使异步代码看起来更像同步代码,通过使用 async 关键字定义异步函数,以及 await 关键字等待异步操作完成。
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = 'Hello, World!';
resolve(data);
}, 1000);
});
}
async function getData() {
const data = await fetchData();
console.log(data); // 输出:Hello, World!
}
getData();
事件监听(EventEmitter)
通过发布订阅模式实现的事件监听,当异步操作完成时触发相应的事件处理函数。
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('data', (data) => {
console.log(data);
});
function fetchData() {
setTimeout(() => {
const data = 'Hello, World!';
emitter.emit('data', data);
}, 1000);
}
fetchData();
Generators 和 Iterators
ES6 引入的生成器和迭代器,结合使用 yield 关键字可以编写更为复杂的异步流程控制逻辑。
function fetchData1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data from source 1');
}, 1000);
});
}
function fetchData2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data from source 2');
}, 1500);
});
}
function* fetchDataGenerator() {
const data1 = yield fetchData1();
console.log(data1);
const data2 = yield fetchData2();
console.log(data2);
return 'All data fetched';
}
// 执行生成器函数
const generator = fetchDataGenerator();
const promise1 = generator.next().value;
promise1.then((data) => {
const promise2 = generator.next(data).value;
promise2.then((data) => {
generator.next(data);
});
});
在这个简单的示例中,fetchDataGenerator 是一个生成器函数,它通过 yield 关键字暂停异步操作。在执行生成器函数时,我们首先获取第一个Promise并等待其解析结果,然后根据这个结果获取第二个Promise,并等待第二个Promise的解析结果。这样就实现了异步流程控制。
RxJS
RxJS 是 ReactiveX 库的 JavaScript 版本,它基于可观察对象的概念,提供了丰富的操作符来处理数据流,适用于处理复杂的异步操作和事件流。
import { from } from 'rxjs';
const dataObservable = from(fetchData());
dataObservable.subscribe({
next: (data) => console.log(data),
error: (err) => console.error(err),
complete: () => console.log('Complete')
});
🌟promise的三个状态
promise是什么
Promise 是 JavaScript 中一种异步编程的解决方案,它可以优雅地处理回调地狱(Callback Hell)问题。
三个状态
Promise 对象代表了一个异步操作的最终完成或失败,并且可以获取其结果。它有以下三个状态:
- pending(等待中): 初始状态,表示异步操作尚未完成,也没有失败。
- fulfilled(已成功): 表示异步操作已经成功完成,此时会触发 resolve() 方法,将异步操作的结果传递出去。
- rejected(已失败): 表示异步操作已经失败,此时会触发 reject() 方法,将异步操作的错误原因传递出去。
Promise 对象一旦进入 fulfilled 或 rejected 状态,就不会再改变状态,这也是 Promise 强大的地方之一。同时,Promise 还提供了 .then() 和 .catch() 方法进行链式调用,方便对异步操作结果进行后续处理。
🌟了解回调地狱以及解决
回调地狱
回调地狱指的是多个嵌套的回调函数,使得代码结构变得混乱和难以维护。下面是一个简单的回调地狱例子:
假设有一个需求是按顺序请求不同接口,并在每个接口请求成功后再进行下一个接口请求,代码可能会变成这样:
getDataFromAPI1(function(response1) {
processData1(response1, function(processedData1) {
getDataFromAPI2(processedData1, function(response2) {
processData2(response2, function(processedData2) {
// 更多嵌套的回调
});
});
});
});
在这段代码中,每个异步操作的结果都需要传递给下一个回调函数处理,导致代码层层嵌套,结构复杂,不易读。这种情况下就会出现回调地狱的问题。
解决方案
为了解决回调地狱问题,可以使用 Promise 或 async/await 等方式来改善代码结构,使其更清晰易读。例如,使用 Promise 改写上面的例子:
getDataFromAPI1()
.then(response1 => processData1(response1))
.then(processedData1 => getDataFromAPI2(processedData1))
.then(response2 => processData2(response2))
.then(processedData2 => {
// 后续操作
})
.catch(error => {
// 错误处理
});
使用 Promise 或 async/await 可以有效避免回调地狱问题,使代码结构更加清晰和易于维护。
🌟完整例子,便于理解
使用 promise 和 async/await 的完整例子,便于理解,小伙伴可以自己修改代码,加深理解
promise用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>promise用法</title>
</head>
<body></body>
<script>
console.log('----------------------');
function buy() {
console.log("开始买笔");
var p = new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("买了笔芯");
resolve("数学作业");
}, 1000);
});
return p;
}
function work(data) {
console.log("开始写作业:" + data);
var p = new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("写完作业");
resolve("作业本");
}, 1000);
});
return p;
}
buy()
.then(function (data) {
// throw new Error("买了坏的笔芯");
work(data).then((data1) => {
console.log(data1 + "提交给老师了!");
});
})
.catch(function (data) {
console.log(data);
});
console.log('-------------');
//输出
// 开始买笔
// 买了笔芯
// Error:买了坏的笔芯
// 如果抛出异常了(代码出错了),那么也不会报错卡死 js,而是会进到这个 catch 方法中。
</script>
</html>
async/await用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>async/await用法</title>
</head>
<body></body>
<script>
// 使用async/await获取成功的结果
// 定义一个异步函数,3秒后才能获取到值(类似操作数据库)
function getSomeThing() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("获取成功");
}, 3000);
});
}
async function test() {
let a = await getSomeThing();
console.log(a);
}
test(); // 3秒后输出:获取成功
</script>
</html>
🌟最后
当我们学习新东西的时候,一定要搞清楚,弄懂;万变不离其宗,思路最重要。小伙伴们如果有什么想法或纠正或添加,可以在评论区或私信我,可以一起交流,共同进步。
✨原创不易,还希望各位大佬支持一下!
👍 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!