详讲同步、异步、推迟关于前端开发中的必备知识

我对于这一部分知识起初也是模棱两可的,经过我大量搜索👨‍💻👨‍💻👨‍💻👨‍💻👨‍💻,现在算是搞明白了。

👨‍🎓👨‍🎓👨‍🎓👨‍🎓 在这里给大家说一下,我目前是一名大学生,哪里不对,敬请指点。

下面我们一起来探讨吧。🙋‍♂️🙋‍♂️🙋‍♂️🙋‍♂️🙋‍♂️🙋‍♂️🙋‍♂️🙋‍♂️🙋‍♂️

📢📢📢:首先需要搞明白什么是同步、异步、推迟

🌟同步、异步、推迟含义

同步

从图中可以看出,同步就像是一条路,但这条路只能容下一个人走,因此前面的人不走快一点,后面的人想走快也没有办法。

所以同步的特点

  • 代码自上而下执行,上一句执行完,才能执行下一句;
  • 即使中间有循环,定时器等,也要等它执行完,才能继续往下执行。
异步(async)

为了解决同步执行效率低的问题,引出了异步编程。

从图中可以看出,异步就像是两条路,两个人互不影响,后面的人想走到前面,也是可以的。

再比如我们做饭:

  1. 洗菜
  2. 切菜
  3. 煲米
  4. 炒菜

这四个步骤:我们总不能在煲米的时候,一直等它熟,然后再去炒菜。煲米的时候,我们也可以执行炒菜,所以这就是异步。

异步的特点

  • 代码可以不用自上而下上一行执行完毕,才能执行下一行;
  • 遇到需要等待的操作,同时往下执行不需要等待的操作,我们可以把需要等待的操作(比如循环,定时器,网络请求等)放入队列中,等待其它不需要等待的操作执行完毕,再依次执行它们。
推迟(defer)

再以异步做饭操作为例,假如我们先把菜炒好了,但是大米还没有煲熟,那么我们还需要等待大米熟了之后才能干饭。这个时候就需要推迟。

从上面图中可以看出,两个人走两条路,其中一个人先到了出口之后,必须等待另一个人先通过之后,自己才能通过。

所以推迟的特点

代码可以同时编译,但是某一行代码执行,必须依赖上一行代码执行完毕才能执行

异步和推迟在加载JS文档和渲染文档中的应用

defer 和 async 都是用于异步加载脚本的属性,它们的主要区别在于脚本加载和执行的时间点不同。具体来说:

  1. defer(推迟): 在 HTML5 中被标准化,它的作用是告诉浏览器立即下载脚本文件,但是推迟脚本的执行,等到文档解析完成后再执行脚本,但是并不能保证多个 defer 脚本的执行顺序。
    <head>
        <script defer src="script.js"></script>
    </head>
    
  2. 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 对象代表了一个异步操作的最终完成或失败,并且可以获取其结果。它有以下三个状态:

  1. pending(等待中): 初始状态,表示异步操作尚未完成,也没有失败。
  2. fulfilled(已成功): 表示异步操作已经成功完成,此时会触发 resolve() 方法,将异步操作的结果传递出去。
  3. 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>

🌟最后

当我们学习新东西的时候,一定要搞清楚,弄懂;万变不离其宗,思路最重要。小伙伴们如果有什么想法或纠正或添加,可以在评论区或私信我,可以一起交流,共同进步。

 ✨原创不易,还希望各位大佬支持一下!
👍 点赞,你的认可是我创作的动力!
⭐️ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!
  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值