首先讲讲ES5与ES6对异步的实现的区别——ES5主要是通过回调函数来实现异步的操作,但是回调函数只能用来处理简单的异步操作,对于复杂的异步操作若仍使用回调函数来解决就可能形成回调地狱,影响代码整洁和维护。

        那么就引出了主题——ES6的异步——promise最初的目的就是解决回调地狱。而对于ES6的异步操作来说主要通过promise和async和await来实现——也是主要实现异步的方法。本篇文章首先就简述了ES6的一些优点,然后着重谈谈怎么去实现ES6的异步。

一、异步的优点
1、异步解决了同步编码时代码阻塞的问题,提高了程序的响应性,提高了用户的体验。

        都知道代码编译时很多情况下为同步编译,代码的执行顺序是从上向下执行,这样的编译方式虽然看着条理清晰,但是当中间一个片段的代码错误或者发生网络请求和文件读写等需要等待的事件时,同步编译就会发生阻塞,影响后面代码的执行,进而影响用户的感受,而异步就像多线程的操作一样,在同步这个主线程运行时,异步代码会被单独放在子线程中运行,不会影响主线程的代码运行,就像后台一样不会影响主程序的使用。

2、高效的利用资源,支持并发处理

        对于网络的资源来说,异步编程可以多程序的同时进行申请资源,可以做到对网络资源的高效利用,也是因为异步类似于多线程的运行方式,使内存可以在等待IO任务(如读取文件、数据库查询)的时候充分利用CPU来处理其他的请求。这样就可以实现多个任务并行执行,大幅度的缩短程序响应的时间。

3、简化复杂的流程控制

        或许有的人看到异步通过ES5的语法去写觉得非常繁琐、复杂,可读性并不是非常的高。但是在ES6中这些都已经被优化掉了,在ES6中有一些方法和语法糖来实现代码的可读性来使异步编程更加的方便和实用。

二、异步的实现方式

在阐述怎么实现异步之前,先补充一点基础的概念来方便理解

promise异步对象分为pending(进行中)、fulfilled(成功)、rejected(失败)三种状态,

状态只能从 pending 转为 fulfilled 或 rejected,且状态一旦改变就不可再变(不可逆)。且一旦返回值确认为rejected则会立即结束程序并抛出错误

                

1、通过promise+then+catch方法来实现

这是最简单的一种方法,通过then方法返回的值来实现程序的连续判断(因为pending的值一经确定之后就无法改变),就像接力赛跑一样,then就是那根接力棒,要拿到上一个人的结果才能继续向下传,成功传递之后才会继续下一个人,如果说中途出现了错误,比如运动员摔倒了或者其他的意外,那么就会有校医——catch来解决这个问题并反应出来。

先创建promise实例

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve("异步操作成功的结果");
    } else {
      reject(new Error("异步操作失败的原因"));
    }
  }, 1000);
});
 
promise
  .then(result => {
    console.log("成功:", result);
    return "下一个异步的输入";
  })
  .then(nextResult => {
    console.log("下一个成功:", nextResult);
  })
  .catch(error => {
    console.error("失败:", error);
  });
 
2、通过promise+async+await来实现

这是现在主流的写法,这种方法更简洁,可读性更强

我们用一个 “烧水→泡茶→泡茶” 的生活场景,对比传统回调嵌套Promise+async/await的实现,直观展示后者的优点。

// 模拟异步操作:烧开水
function boilWater(callback) {
  setTimeout(() => {
    console.log("水烧开了");
    callback(); // 完成后调用下一步
  }, 1000);
}

// 模拟异步操作:洗茶杯
function washCup(callback) {
  setTimeout(() => {
    console.log("茶杯洗好了");
    callback(); // 完成后调用下一步
  }, 1000);
}

// 模拟异步操作:泡茶(依赖前两步)
function makeTea() {
  setTimeout(() => {
    console.log("茶泡好了,可以喝了");
  }, 1000);
}

// 执行流程(回调嵌套)
boilWater(() => {
  washCup(() => {
    makeTea();
  });
});
// 用Promise封装异步操作
function boilWater() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log("水烧开了");
      resolve(); // 成功后通知
    }, 1000);
  });
}

function washCup() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log("茶杯洗好了");
      resolve();
    }, 1000);
  });
}

function makeTea() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log("茶泡好了,可以喝了");
      resolve();
    }, 1000);
  });
}

// 用async/await组织流程
async function makeTeaProcess() {
  try {
    await boilWater(); // 等待水烧开
    await washCup();   // 等待茶杯洗好
    await makeTea();   // 前两步完成后泡茶
  } catch (error) {
    console.error("出错了:", error); // 统一捕获所有错误
  }
}

// 执行
makeTeaProcess();

可以看到在调用函数相同的情况下,async和await的写法能更加直观的体现出代码运行的逻辑,并具有强大的可读性,并且通过try和catch的结合来捕获错误,实现异步编程的清晰的错误处理。

并且在之后的接口的编码与处理之中,async和await的优点会更加明显。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐