ES6异步处理

3 阅读8分钟

ES6异步处理

Promise异步处理

首先我们讲之前要了解一个概念:什么是同步和异步?

我们引入一个很简单的例子来解释一下

console.log('任务 1 开始');
for (let i = 0; i < 1000000000; i++) {} // 一个超级耗时的循环
console.log('任务 1 结束'); // 必须等上面的循环执行完,才会打印这行
console.log('任务 2');

你看 当我们执行这些语句的时候 我们都是按照顺序一步一步来的 同步进行的

一句话概括 同步就是任务按顺序依次执行,执行完一个再执行下一个。会阻塞后续代码!!!

注意这个阻塞后续代码是什么意思呢 通俗地来讲就是上一个语句如果没有执行完 下一段代码就不会执行!

那什么是异步呢?

一句话概括 发起一个任务后,不必等待它完成,可以立即继续执行后续代码。等这个任务完成后,再通过回调函数等方式处理结果。不会阻塞后续代码!!!

那么又有同学要问了 涛哥涛哥 异步有啥用啊?

我现在在举一个例子 定时器

console.log('任务 1: 开始');

// 发起一个异步任务 (定时器)
setTimeout(function() {
  console.log('任务 2: 这是一个异步任务,2秒后执行');
}, 2000); // 2000毫秒后,回调函数会被执行

console.log('任务 3: 结束');

输出结果:

任务 1: 开始
任务 3: 结束
(等待大约2秒...)
任务 2: 这是一个异步任务,2秒后执行

就算定时器设置了0毫秒,即使定时器设置了 0 毫秒,它的回调函数也会被放到最后执行:

console.log('开始');

setTimeout(function() {
  console.log('异步回调');
}, 0); // 即使是0秒,也是异步

console.log('结束');

输出结果:

开始
结束
异步回调

通过我刚刚写的代码 你能理解异步是什么意思了吗

现在知道异步有什么用了吧

如果没有异步 那么真实的工作环境中所以操作都是同步的 那么像网络请求和定时器这种会消耗时间的操作就会阻塞页面的渲染和交互,啥意思呢,就比如你要抢个演唱会票,当你按下抢票按钮的一瞬间网络请求开始了,结果因为太耗时,你等了好久页面都无反应,你气不气,是我我都气死了。

那么大家现在要记住一个结论:异步任务的执行一定是在触发了同步信息之后再执行的 就好像我刚刚给你举得例子(就是那个0毫秒的定时器 它就算是0秒 那也是最后才输出的)

常见的异步有:定时器 Ajax(这个以后给大家讲)

了解完同步和异步 我们现在来讲promise为什么要出现

如下

console.log('任务1')
console.log('任务2')
setTimeout(()=>{
	console.log('任务3')
})
console.log('任务4')

这里我们做一个假设哈 就是任务4得需要任务3的结果才能完成 那么这个时候就互相矛盾了 因为任务3是异步的 是最后一个完成的 但是任务4需要任务3的结果才能完成 我勒个骚钢 那不是炸了吗 陷入死循环了

聪明的你一定想得到:那我把任务4丢进去包含着任务3的定时器不就可以了吗 对 你这样子想没问题 就我给你的例子来说确实可以这样子解决 但是你到了真正的工作环境中 有那么简单的语句给你划水吗 基本上都是复杂的逻辑实现的函数 那可就不一样了 你难道一直嵌套一直嵌套吗 屎山代码就是这样子来的 显然不合理

那么这个时候 promise出现了 它能够将这种复杂的嵌套式的异步结构书写为一种更为接近同步的写法!

那么我们开始来讲解promise

如何创建一个promise呢 其实promise是Es6提供的一个类 我们需要像java那样子来创建一个实例来使用它

const p1 = new Promise((resolve, reject) => {
  reject('失败')  // 这个先执行
  resolve('成功')  // 这个不会执行,因为 Promise 状态一旦改变就不能再改变
})

p1.catch(err => {
  console.log(err)  // 输出: "失败"
})

p1.then(res => {
  console.log(res)  // 不会执行,因为 Promise 被拒绝了
})

输出结果:

失败

你可能会看的有点蒙蒙的 我来解释一下这个执行过程

第一步:我们创建了p1这个promise实例 执行器函数立即执行

在这里我解释一下:你可以简单的理解resolve是成功的状态 而reject是失败的状态

而当你在函数体里面决定了promise的状态之后 promise的状态就不能再改变了 这是你要注意的

如何判断状态呢 其实就是看哪个语句先提出 就比如上面那个reject语句先提出 那么p1的状态就是reject 反之一样

**第二步:**reject(‘失败’)被调用了,就如同我刚刚讲的那样子,Promise 状态变为 rejected

第三步:resolve('成功') 被调用但无效,因为 Promise 状态已经确定

第四步:大家注意这里有两个细节

​ 1. .catch()是属于rejectd状态的回调函数

​ 2. .then()是属于resolve状态的回调函数

​ 所以.then()不会被执行,因为promise被拒绝了(也就是reject状态)

最后一步:在当前同步代码执行完后,微任务队列中的回调开始执行

好了 了解完promise的写法和作业 接下来我们来学习Async

async 和 await

首先在讲解之前,我们要先了解一下Promise和Async/Await 之间的关系,它们不是替代关系,而是相辅相成、共同演进的关系。

官方地来讲:

Async/Await 是基于 Promise 的语法糖,它的出现是为了让异步代码的书写和阅读更像同步代码,从而彻底解决“回调地狱”问题。你可以理解为:Async/Await 是使用 Promise 的更优雅、更简洁的一种方式

有人就要问了 语法糖是啥意思 其实语法糖指的是:一种语法上的甜头,他让代码更加容易书写和阅读,但是并没有引入新的功能。它只是用一种更简洁、更优雅的方式,来表达语言中已有的、可能更复杂的逻辑。

所以Async/Await只是基于promise的更简洁的写法

我们来比较一下二者的差别

首先我们给出来promise的写法:

function fetchData() {
  return new Promise((resolve, reject) => {
    // 模拟一个异步操作,比如网络请求
    setTimeout(() => {
      const data = ‘Some data’;
      // 如果成功
      if (data) {
        resolve(data); // 将Promise状态变为“已完成”(fulfilled)
      } else {
        reject(‘Error fetching data’); // 将Promise状态变为“已拒绝”(rejected)
      }
    }, 1000);
  });
}

// 使用Promise
fetchData()
  .then(data => {
    console.log(‘Success:’, data);
    return processData(data); // 可以返回新的Promise,继续链式调用
  })
  .then(processedData => {
    console.log(‘Processed:’, processedData);
  })
  .catch(error => {
    console.error(‘Error:’, error);
  });

然后我们再使用async 和 await来重写上面的代码

// 声明一个异步函数
async function getData() {
  try {
    // await 会等待 fetchData 这个 Promise 成功解决(resolve),并取出它的值
    const data = await fetchData(); // 看起来像是同步赋值
    console.log(‘Success:’, data);

    // 可以继续 await 另一个 Promise
    const processedData = await processData(data);
    console.log(‘Processed:’, processedData);

    return processedData; // async函数返回的也是一个Promise

  } catch (error) {
    // 使用 try...catch 来捕获所有等待过程中的错误
    console.error(‘Error:’, error);
  }
}

// 调用 async 函数
getData().then(result => console.log(‘Final result:’, result));

你看 使用async是不是显得更加简洁了

我们来重点介绍一下

核心思想:

  • async:声明一个函数是异步的。它会自动让这个函数返回一个 Promise 对象。如果函数本身返回一个值,这个值会被包装成一个 resolved 状态的 Promise。
  • 自动返回 Promise:一个被 async 声明的函数,无论你返回什么,它都会自动帮你包装成一个 Promise 对象。这是最关键的特性之一。
    • 如果你返回一个普通值(如 return 'hello';),它等价于 return Promise.resolve('hello');
    • 如果你抛出一个错误(如 throw new Error(...)),它等价于 return Promise.reject(error);
    • 如果你返回一个 Promise,那就直接返回这个 Promise。
  • await:只能在 async 函数内部使用。它会“暂停”异步函数的执行,等待一个 Promise 对象 resolve,然后返回其结果。它让异步操作的等待过程看起来就像是同步的一样。

我使用ai让他整了一个执行流程可视化 你可能有点晕晕的 但是这个有助于你理解

时间开始
|--> 调用 getData(),它立即返回一个 Promise(状态:pending)
|    |--> 进入 getData 函数内部
|    |--> 执行 await fetchData(),函数执行暂停,“让出”主线程
|         |--> (主线程空闲,可以处理其他任务)
|         |--> ... 一段时间后,fetchData 的 Promise resolve 了 ...
|         |--> getData 函数恢复执行,将获取的值赋给 data
|         |--> 打印 'Success: ...'
|         |--> 执行 await processData(data),函数再次暂停
|              |--> ... 一段时间后,processData 的 Promise resolve 了 ...
|              |--> getData 函数恢复执行,将获取的值赋给 processedData
|              |--> 函数 return processedData
|              |--> 导致 getData() 返回的那个 Promise 状态变为 fulfilled,值为 processedData
|--> 外部,.then() 的回调被触发,result 就是 processedData
|--> 打印 'Final result: ...'
时间结束

有人会问这个fetchDate()和processDate()是哪里冒出来的 你就把他当成一个能返回一个promise函数就可以了 现在应该能理解了吧!

好 本节课就结束了 完结撒花!!!

接下来我会完成Es6中的代理与模块md