【异步系列二】Promise原理及执行顺序详解

前言

Promise 是 javascript 中非常重要的一环,熟悉它是必须的,而且在面试中也常常会问到相关面试题。
在了解 Promise 之前,需要了解什么是异步编程,可以参考我的一篇文章:JavaScript 中异步编程原理解析

1. Promise 简介

Promise 是 JavaScript 的一个内置对象,在语法上是一个构造函数,该构造函数只有一个参数,是一个函数。初始化时,这个函数在构造之后会直接被异步运行,所以称之为起始函数。

起始函数包含两个参数: resolve 和 reject.

resolve 和 reject 都是函数,其中调用 resolve 代表一切正常,是成功时的回调; reject 是出现异常是调用的。

Promise 对象代表一个异步操作。

有三种状态:

  • pending(进行中):初始状态,既不是成功,也不是失败状态
  • resolved (又称fufilled)(已成功):意味着操作成功完成
  • rejected(已失败):意味着操作失败。

一个 promise 对象只能改变一次状态,成功或者失败后都会返回结果数据。

Promise 的状态一旦改变,就不能再次改变。因此,一旦执行了第一个 resolve 函数,就不会再执行其他的 resolve函数或者 reject函数。

// 初始状态为 pending
let pro = new Promise((resolve, reject) => {
	resolve(value);  // 若执行成功函数,则状态变为 resolved
	reject(value);   // 若执行失败函数,则状态变为 rejected
}) ;
pro.then(fn1, fn2);  // fn1: 成功时的执行函数(即Promise状态变为resolved时调用),fn2:失败时的执行函数(Promise状态变为 rejected 时调用)

写几个有关 Promise 的例子,有助于理解。

当 Promise 刚被new 出来,是pending状态

const p = new Promise((resolve, reject) => {
    console.log(111)
})
console.log(p)

在这里插入图片描述

【PromiseState】就是 Promise 现在的状态

当调用 resolved 函数,状态立刻变成 fulfilled 成功

在这里插入图片描述

当调用 reject 函数,状态又会变成 rejected 失败

在这里插入图片描述

如果两个回调函数都调用,那么则是谁先调用先返回谁的状态

在这里插入图片描述

当我们 new 一个 Promise 时,就已经开始自动执行函数。Promise 是同步的,但 then 是异步的,要注意区分。

在这里插入图片描述

2. Promise 的基本流程

在这里插入图片描述

3. Promise 的实例方法

Proimse 拥有两个是方法 then() 和 catch()

then() 方法

成功和失败的回调函数我们是通过 then() 添加,在Promise 状态改变时分别调用。

then() 方法可以接收两个参数,且通常都是函数,第二个参数可忽略。

then() 方法会返回一个新的 promise

catch() 方法

该方法相当于最近的 then() 方法的第二个参数,指向 reject 的回调函数,另一个作用是:在执行resolve 回调函数时,如果出错,抛出异常,不会停止运行,而是进入 catch() 方法中

注意: catch 只捕获最近的 then 的回调函数,前面的 then 的执行不成功结果,有后面的 reject 回调函数执行,如果没有后续 then 回调函数执行,则会被 catch 捕获执行。

4. 关于 Promise 的执行顺序

执行顺序:主任务 > 微任务(then()、catch()) > 宏任务 (setTimeout、requestAnimationFrame)

当我们 new 一个 Promise 时,传入的回调函数为同步代码,会立即执行,而 .then() .catch() 里面的为 异步微任务。

为了避免意外,即使是一个已经变成 resolve 状态的 Promise,传递给 then() 的函数也是会被异步调用。

Promise.resolve().then(() => console.log(2));
console.log(1)

// 输出结果为:
// 1
// 2

下面以一个例子来详细说明 Promise 的执行顺序,大家先认真思考下,然后对比下面的答案。

const p = new Promise((resolve, reject) => {
	console.log(1)

	const q = () => new Promise((resolve, reject) => {
		console.log(3)
		resolve(4)
	})

	const r = () => new Promise((resolve, reject) => {
		console.log(7)
		resolve(8)
	})

	setTimeout(() => {
		console.log(10)
	}, 0)
	
	resolve(2)
	
	q().then(res => {
		console.log(res)
		setTimeout(() => {
			console.log(6)
		},0)
	})

	r().then(res => {
		console.log(res)
		setTimeout(() => {
			console.log(9)
		},0)
	})
})

console.log(5)
p.then(res => {
	console.log(res)
})

/**
	Promise 一旦被声明就会立即执行,此时内部的代码会按照顺序执行,但是 resolve(res) 的代码会是异步,所以当 Promise() 中的同步代码会先执行, resolve() 的代码会后执行,resolve() 之后的代码也先执行,Promise 中的所有同步和异步代码执行完毕之后,resolve() 才执行。
	
	上面这段代码的执行顺序如下所示:

	我们将其分为三个阶段来分析这段代码的执行。

	第一阶段:
		同步代码先执行。
		因为同步代码最先执行,又因为 Promise 一旦被调用就会立即执行所以会打印 --- 1
		resolve(2) 作为异步,仍在等待同步代码执行完毕,且 p 内部的全部代码(同步、异步)执行完成之后才会被执行。
		然后声明了一个 q,r 的Promise,然后调用q,同步代码立即执行,打印 --- 3
		resolve(4), 作为异步仍在等待同步代码执行完毕,且 q 内部的代码全部执行完成之后才会被执行。
		同理,打印 r  中的同步代码, resolve(8) 作为异步等待。  ----------- 7
		到这里 p 内部的同步代码执行完毕,此时跳出 p 执行外部的同步代码,打印 ----5

		此时代码执行顺序为:1  3  7  5

	第二阶段:
		Promise() 中异步的 resolve() 执行
		当同步执行之后就是 resolve() 异步的代码, p() 先执行,但是 p() 自己的resolve() 必须等到其内部全部的异步 resolve() 执行完毕后才能够执行。
		所以此时应该是先执行的是 q().then(), 打印 ----- 4
		然后打印的是 r().then(),打印 --------- 8
		然后 p() 内部的 resolve() 异步全部执行完毕,最后就要执行 p().then(),打印 ----- 2
		
		此时代码执行顺序为:4  8  2

	第三阶段:
	setTimeout() 异步函数的执行。
	由于 Promise 是JS引擎的内部任务,而 setTimeout 是浏览器外部的API,所以 Promise 的优先级高于 setTimeout,也就是因此,只有所有的 Promise() 内的 resolve() 执行完成之后,才会执行 setTimeout 函数。
	而 setTimeout 函数的执行顺序就看它被调用的先后顺序
	从上到下一次调用,打印 ----- 10  6  9 	
*/

执行结果如下所示:

在这里插入图片描述

5. Promise执行顺序思路

1、按照 同步先行,异步靠后 原则,阅读代码时,先分析同步代码和异步代码。Promise对象虽然是微任务,但 new Promise 时的回调函数(起始函数)是同步执行的。

2、在 resolve 执行时,Promise 对象的状态变更为 已完成,所以 then() 函数的回调被注册到 微任务事件中,此时并不执行,所以接下来会跳出 p 执行外部的同步代码,输出 5。

3、同步代码执行结束后,观察异步代码的宏任务微任务。微任务会优先执行,Promise 为微任务,setTimeout 为宏任务

关于宏任务微任务 的执行顺序,在我的* 【异步系列五】博客中解释的更清楚,有需要的可以看下。

博客地址:【异步系列五】关于async/await与promise执行顺序详细解析及原理详解

  • 18
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
回答: Promise执行顺序可以总结如下:首先,Promise对象会立即执行,即使没有设置回调函数。其次,Promise对象的状态会根据异步操作的结果进行改变。当异步操作成功完成时,Promise的状态会变为fulfilled;当异步操作失败时,Promise的状态会变为rejected。接下来,根据Promise对象的状态,会执行相应的回调函数。如果Promise对象的状态为fulfilled,会执行then方法中的回调函数;如果Promise对象的状态为rejected,会执行catch方法中的回调函数。需要注意的是,catch方法只捕获最近的then的回调函数,前面的then的执行不成功结果会被后面的reject回调函数执行,如果没有后续then回调函数执行,则会被catch捕获执行。总之,Promise执行顺序是根据异步操作的结果来决定状态的改变,并根据状态执行相应的回调函数。\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [Promise 实现原理](https://blog.csdn.net/shi851051279/article/details/113922007)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【异步系列Promise原理执行顺序详解](https://blog.csdn.net/qq_41131745/article/details/126974598)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值