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...
            });
        });
    });
});

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

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

相关推荐
Pedantic5 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘6 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆6 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师7 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆7 小时前
VSCode自动格式化三要素
前端
爱勇宝8 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen8 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user205855615181310 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode10 小时前
Redis 在生产项目的使用
前端·后端