JS:一篇文章带你搞懂什么是异步

前言

我们先来说说什么是异步,异步操作并不仅仅局限于编程和计算机科学领域,我们在日常生活中也经常会遇到异步的情况。我们用这几个有趣的例子来帮大家理解一下:

  1. 电子邮件通信: 发送电子邮件是一种异步操作。你发送了一封邮件之后,并不需要立即等待对方回复,而是可以继续进行其他活动。当对方准备好回复时,他们会发送回复。
  2. 邮寄信件: 将信件寄出后,并不会立即送达,而是需要一些时间。寄件人不需要等待信件到达目的地,而可以进行其他活动。
  3. 预约和会议: 安排预约或会议也是一种异步操作。当你预约或安排会议时,并不需要等待对方立即确认,而是可以进行其他安排。对方会在适当的时候回复或确认。
  4. 交通信号灯: 交通信号灯的变化是异步的。当你等待红灯变绿时,你并不需要一直停车等待,而可以进行其他事情,只有当信号变绿时才需要继续行驶。
  5. 购物: 在网上购物时,提交订单后并不需要立即等待商品送达。你可以继续进行其他活动,而在适当的时间收到商品。

异步

异步(Asynchronous)是指在不同步或不同时的时间执行任务或操作的方式。在编程和计算机科学领域中,异步通常用来描述一种处理事件和任务的机制,其中不需要等待一个操作完成,而是可以同时执行其他操作。

在同步编程中,程序的执行是按照顺序一步一步进行的,每个操作都需要等待上一个操作完成后才能执行。这可能导致程序在等待某些操作完成的同时处于空闲状态。

相比之下,在异步编程中,程序可以继续执行其他操作而不必等待某个操作完成。当异步操作完成时,通常通过回调函数、Promise对象或异步/等待(async/await)等机制来处理结果。这种方式有助于提高程序的响应性和效率,特别是在处理网络请求、文件操作或其他涉及等待时间的任务时。

JavaScript(JS)是一种异步编程语言。JavaScript最初设计为一种用于在浏览器中操作DOM(文档对象模型)的脚本语言,因此异步编程对于处理用户界面的交互、网络请求和其他事件至关重要。

我们先来看一个异步的例子:

假如我们去饭店吃饭时,我们点好了菜之后,厨师开始炒菜,那我们在厨师炒菜的时候一般会玩玩手机,刷刷抖音,而不是坐在那里一动不动等着厨师上菜

js 复制代码
function a (){ 
    setTimeout(function(){
         console.log('刷会抖音')
        },1000)
    }
    a()
    function b (){
         console.log("菜做完啦!")
    }
    b()

我们先来看看输出结果:

我们可以看到输出顺序为菜做完啦过了一秒钟之后再打印刷会抖音

这段代码涉及到异步执行的概念,主要是通过setTimeout函数创建了一个定时器,该定时器会在指定的时间间隔后执行回调函数。让我们一步一步解释这段代码:

js 复制代码
function a (){ 
    setTimeout(function(){
        console.log('刷会抖音');
    }, 1000);
}

这是一个函数 a,其中使用 setTimeout 函数创建了一个定时器。这个定时器会在 1000 毫秒(即1秒)后执行一个匿名函数,而这个匿名函数的内容是输出 '刷会抖音'

然后,你调用了函数 a

js 复制代码
a();

由于 setTimeout 是异步的,它会在后台计时,不会阻塞程序的执行。所以,调用 a 后,程序会立即继续往下执行,而不会等待定时器的回调执行。

接着,你有另一个函数 b

js 复制代码
function b (){
    console.log("菜做完啦!");
}

这个函数 b 用于输出信息 "菜做完啦!"

最后,你调用了函数 b

js 复制代码
b();

在整个程序执行过程中,b 函数会立即执行,而不会等待定时器的回调函数。因此,你可能会在控制台看到类似于以下的输出:

js 复制代码
菜做完啦!
刷会抖音

这是因为 b 函数会立即执行,而 a 函数中的定时器回调函数会在1秒后执行。这展示了异步执行的特性,使得程序能够继续执行而不必等待定时器完成。

那如果我们想要刷会抖音菜做完啦之前执行,可以做些什么呢?

回调函数

js 复制代码
function a(cb){
    setTimeout(() => { 
        console.log("刷会抖音");
        cb() 
    },1000)
}

function b(){ 
    setTimeout(() => {
        console.log("菜做完啦");
    }, 0);
}

  a(b)

这段代码包含两个函数 ab,并通过将函数 b 作为回调函数传递给函数 a 来展示异步操作和回调函数的概念。让我们一步一步解释:

js 复制代码
function a(cb) {
    setTimeout(() => { 
        console.log("刷会抖音");
        cb();
    }, 1000);
}

这是一个函数 a,它接受一个回调函数 cb 作为参数。在函数体内,使用 setTimeout 函数创建了一个定时器,它会在 1000 毫秒(即1秒)后执行一个匿名函数。这个匿名函数的内容包括输出 '刷会抖音' 和调用回调函数 cb

然后,你有另一个函数 b

js 复制代码
function b() { 
    setTimeout(() => {
        console.log("菜做完啦");
    }, 0);
}

这是另一个函数 b,它使用 setTimeout 创建了一个定时器,该定时器在 0 毫秒后执行一个匿名函数,输出 '菜做完啦'

最后,我们调用了函数 a,将函数 b 作为回调函数传递进去:

js 复制代码
a(b);

在这里,调用 a 会启动一个定时器,在 1 秒后执行 '刷会抖音' 并调用回调函数 cb,而 cb 此时指向函数 b。所以,即便函数 b 的定时器设置为0秒,它的执行会在函数 a 的定时器之后,但在整体执行上并没有造成延迟。

因此,我们会在控制台看到以下的输出:

js 复制代码
刷会抖音
菜做完啦

这展示了如何通过回调函数来管理异步操作的执行顺序。

回调地狱

回调地狱(Callback Hell)是指在异步编程中,多个嵌套的回调函数形成的代码结构复杂、难以理解和维护的情况。这通常发生在处理多个异步操作,其中一个操作的结果需要作为参数传递给另一个操作,依此类推。

以下是一个简单的例子,展示了回调地狱的情况:

js 复制代码
getDataFromServer(function (data) {
    process1(data, function (result1) {
        process2(result1, function (result2) {
            process3(result2, function (result3) {
                // More nested callbacks...
            });
        });
    });
});

在这个例子中,每个异步操作的结果都需要传递给下一个操作,导致代码缩进层次加深,可读性变差。这种结构会使代码难以维护,容易出现错误,并且不符合良好的代码风格。

在下一篇文章中我们会讲如何用更好的方式去处理异步

相关推荐
持久的棒棒君2 小时前
npm安装electron下载太慢,导致报错
前端·electron·npm
yours_Gabriel3 小时前
【java面试】微服务篇
java·微服务·中间件·面试·kafka·rabbitmq
crary,记忆4 小时前
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
前端·webpack·angular·angular.js
漂流瓶jz5 小时前
让数据"流动"起来!Node.js实现流式渲染/流式传输与背后的HTTP原理
前端·javascript·node.js
SamHou05 小时前
手把手 CSS 盒子模型——从零开始的奶奶级 Web 开发教程2
前端·css·web
我不吃饼干5 小时前
从 Vue3 源码中了解你所不知道的 never
前端·typescript
开航母的李大5 小时前
【中间件】Web服务、消息队列、缓存与微服务治理:Nginx、Kafka、Redis、Nacos 详解
前端·redis·nginx·缓存·微服务·kafka
Bruk.Liu5 小时前
《Minio 分片上传实现(基于Spring Boot)》
前端·spring boot·minio
鱼樱前端6 小时前
Vue3+d3-cloud+d3-scale+d3-scale-chromatic实现词云组件
前端·javascript·vue.js