javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JS Promise与事件循环

JavaScript Promise与事件循环的深入理解

作者:郑在努力中

事件循环是JavaScript在单线程环境中实现并发的核心机制,它协调调用栈、任务队列与微任务队列,保证异步代码以可预测的顺序执行,这篇文章主要介绍了JavaScript Promise与事件循环的相关资料,需要的朋友可以参考下

Promise

一、Promise 是什么?(定义)

官方说法:Promise 是一个用于表示异步操作最终结果的对象。

人话版:Promise 就是一个“承诺”:现在结果可能还没出来,但将来一定会给你一个结果(成功 or 失败)。

比如:请求接口(不知道什么时候返回)、读取文件、定时任务、图片上传等

这些都需要 Promise。

二、为什么需要 Promise?

在 Promise 出现前,我们用的是 回调函数

ajax(url, success => {
  ajax(url2, success2 => {
    ajax(url3, success3 => {
      ...
    });
  });
});

这叫:回调地狱(Callback Hell)难读、难维护、难错误处理

Promise 的目标:让异步代码写得像同步一样清晰。

三、Promise 的三种状态

Promise 永远处于以下三种状态之一:

状态

含义

pending

进行中

fulfilled

成功

rejected

失败

特点:

四、Promise 基本用法

1. 创建 Promise

使用 new Promise 来包装异步逻辑。它接收一个函数,这个函数有两个参数:resolve(成功时调用)和 reject(失败时调用)。

const p = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    resolve('成功结果');
    // reject('失败原因');
  }, 1000);
});

参数说明:

2. 使用 Promise:then / catch

p.then(result => {
  console.log('成功:', result);
}).catch(error => {
  console.log('失败:', error);
});

五、Promise 执行流程(非常重要)

new Promise((resolve, reject) => {
  resolve(100);
})
.then(res => {
  return res + 1;
})
.then(res => {
  console.log(res); // 101
});

流程是:

注意: then 是可以链式调用的

六、常用实例方法很常用

1. then

处理成功结果:

promise.then(res =>{})

2. catch

捕获错误:

promise.catch(err => {})

等价于:

promise.then(null, err => {})

3. finally

无论成功失败都会执行:

promise.finally(() => {
  console.log('请求结束,关闭 loading');
});

七、常用静态方法

这些是挂在 Promise 类上的方法:

1、Promise.resolve(value)

作用:快速返回一个 成功态 Promise

行为规则

Promise.resolve(普通值)      // fulfilled,值就是这个值
Promise.resolve(Promise对象) // 直接返回这个 Promise
Promise.resolve(thenable对象) // 会执行 then

示例 1:包装同步值为异步

Promise.resolve(10).then(v => {
  console.log(v); // 10
});

示例 2:统一函数返回 Promise

function getData() {
  if (cache) {
    return Promise.resolve(cache);
  }
  return fetch('/api').then(r => r.json());
}

常用于:让函数无论同步/异步都返回 Promise

2、Promise.reject(reason)

作用:返回一个 失败态 Promise

Promise.reject('error').catch(err => {
  console.log(err); // error
});

实际场景:参数校验失败时直接中断:

function submit(data) {
  if (!data.username) {
    return Promise.reject('用户名不能为空');
  }
  return fetch('/submit');
}

3、Promise.all(promises)

核心规则:全部成功 → 成功||任何一个失败 → 直接失败

示例

const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);

Promise.all([p1, p2, p3])
  .then(res => {
    console.log(res); // [1, 2, 3]
  });

如果有一个失败:

const p1 = Promise.resolve(1);
const p2 = Promise.reject('错了');
const p3 = Promise.resolve(3);

Promise.all([p1, p2, p3])
  .catch(err => {
    console.log(err); // "错了"
  });

常见真实业务,页面初始化:

Promise.all([
  fetchUser(),
  fetchMenu(),
  fetchPermission()
]).then(([user, menu, permission]) => {
  renderPage();
});//数组的解构赋值

4、Promise.race(promises)

规则:谁先完成(成功或失败)就采用谁的结果

示例:模拟超时控制

function timeout(ms) {
  return new Promise((_, reject) => {
    setTimeout(() => reject('请求超时'), ms);
  });
}

Promise.race([
  fetch('/api/data'),
  timeout(3000)
])
.then(res => console.log('成功'))
.catch(err => console.log(err));

实际意义:接口 3 秒内没返回就报超时

5、Promise.allSettled(promises)

规则:不管成功还是失败,等所有 Promise 都结束

返回格式固定:

[
  { status: 'fulfilled', value: 成功值 },
  { status: 'rejected', reason: 失败原因 }
]

示例

const p1 = Promise.resolve(100);
const p2 = Promise.reject('失败了');

Promise.allSettled([p1, p2])
  .then(results => {
    console.log(results);
  });

真实场景:多个接口加载,允许部分失败:

Promise.allSettled([
  loadBanner(),
  loadRecommend(),
  loadAds()
]).then(results => {
  const successData = results
    .filter(r => r.status === 'fulfilled')
    .map(r => r.value);

  render(successData);
});

6、Promise.any(promises)

规则:有一个成功就成功,全部失败才失败

示例

Promise.any([
  Promise.reject('A失败'),
  Promise.reject('B失败'),
  Promise.resolve('C成功')
]).then(res => {
  console.log(res); // C成功
});

如果全部失败:

Promise.any([
  Promise.reject('A'),
  Promise.reject('B')
]).catch(err => {
  console.log(err instanceof AggregateError); // true
  console.log(err.errors); // ['A', 'B']
});

真实场景:多 CDN 加载资源:

Promise.any([
  loadFromCDN1(),
  loadFromCDN2(),
  loadFromCDN3()
]).then(url => {
  console.log('成功加载:', url);
});

7、六个方法对比总结

方法

成功条件

失败条件

典型用途

resolve

直接成功

不会失败

包装值

reject

不会成功

直接失败

快速中断

all

全部成功

任一失败

多接口并发

race

第一个完成

第一个失败

超时控制

allSettled

不关心

不关心

允许部分失败

any

任一成功

全部失败

多源容灾

八、async / await 与 Promise 的关系

async function getData() {
  const res = await fetch('/api');
  return res.json();
}

本质上:async 函数永远返回 Promise,await 就是 then 的语法糖

等价于:

function getData() {
  return fetch('/api').then(res => res.json());
}

九、真实业务中的 Promise

1. 请求接口

axios.get('/api/user')
  .then(res => setUser(res.data))
  .catch(err => showError());

2. loading 状态控制

setLoading(true);

fetchData()
  .finally(() => setLoading(false));

3. 顺序执行异步任务

async function run() {
  await step1();
  await step2();
  await step3();
}

事件循环

拆解成三个核心角色:调用栈(Call Stack)任务队列(Task Queue) 和 微任务队列(Microtask Queue)

1. 核心角色扮演

2. 事件循环的执行流程

可以把事件循环想象成一个永远在旋转的转盘,它的工作逻辑非常死板:

避坑点

3.例子

console.log('1. Script Start');

setTimeout(() => {
    console.log('2. setTimeout 1');
    Promise.resolve().then(() => {
        console.log('3. Promise in setTimeout');
    });
}, 0);

async function async1() {
    console.log('4. async1 Start');
    await async2();
    console.log('5. async1 End'); 
}

async function async2() {
    console.log('6. async2');
}

async1();

new Promise((resolve) => {
    console.log('7. Promise Constructor');
    resolve();
}).then(() => {
    console.log('8. Promise then');
});

setTimeout(() => {
    console.log('9. setTimeout 2');
}, 0);

console.log('10. Script End');

深度解析

最终答案

1 -> 4 -> 6 -> 7 -> 10 -> 5 -> 8 -> 2 -> 3 -> 9

总结 

到此这篇关于JavaScript Promise与事件循环的深入理解的文章就介绍到这了,更多相关JS Promise与事件循环内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文