JS宏进阶:Promise与微任务队列

在 JavaScript 中,Promise对象和微任务队列是事件循环机制的重要组成部分,用于处理异步操作。理解它们之间的关系可以帮助你更好地掌握 JavaScript 的异步编程模型。

一、Promise基础用法

它是一种用于表示异步操作最终完成或失败的对象,换句话说,Promise对象代表一个尚未完成但预期将来会完成的异步操作。它有三种状态:

  • Pending(进行中):初始状态,既不是成功也不是失败
  • Fulfilled(已成功):操作成功完成。
  • Rejected(已失败):操作失败。

注意:Promise的状态只能从pending变为fulfilled或rejected,并且一旦状态改变,就不会再变。

示例如下:

const myPromise = new Promise((resolve, reject) => {
    // 执行异步操作
    let isSuccess = true; // 假设一个成功的条件
    if (isSuccess) {
        resolve('Operation succeeded');
    } else {
        reject('Operation failed');
    }
});

myPromise.then((value) => {
    console.log(value); // 输出:Operation succeeded
}).catch((error) => {
    console.log(error); // 输出:Operation failed (如果失败)
});

效果图如下所示:

注意,Promise对象拥有如下两个特性:

  • Promise对象的状态不受外界影响,只能通过resolve和reject函数来改变
  • Promise对象提供了一种链式调用的方式,可以通过 .then() 和 .catch() 方法来处理异步操作的结果或错误。

二、微任务队列

1、简介

在 JavaScript 的事件循环中,任务队列主要分为宏任务队列和微任务队列:

  • 宏任务:包括整体脚本、setTimeout等全局函数
  • 微任务:包括 Promise 回调、MutationObserver 等

注意:在WPS编辑器中,setTimeout 与 MutationObserver(监听 DOM 树的更改的对象)均不支持。

2、Promise 与微任务的关系

当一个 Promise 状态变为 Fulfilled 或 Rejected后,使用 .then()、.catch() 或 .finally() 注册的回调不会立即执行,而是被放入微任务队列中。这保证了微任务的执行优先级(相对于宏任务)。

Promise 回调的执行顺序示例代码:

console.log('script start');

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

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

console.log('script end');

??,相信你发现了,示例中有一个setTimeout,不是说WPS编辑器不支持吗?没关系,我们可以通过异步来模拟浏览器中 setTimeout 的行为,示例如下:

function setTimeout(callback, delay) {
	if (delay < 0) throw new Error("delay: is must more then 0.");
	let start = Date.now();
	let loop = () => {
	    if (Date.now() - start >= delay) {
	        try {callback();} catch(er) {throw new Error(er.message);}
	    } else {
	            Promise.resolve().then(loop); // 添加到微任务队列
	    }
	};
	loop();
}

注意:这个自定义的 setTimeout 使用的是 Promise.resolve().then(loop) 来模拟异步行为,这意味着它会在当前宏任务完成后立即在下一个微任务队列中执行检查。
由于 Promise.then 是微任务,因此它会在当前宏任务的同步代码执行完之后立即执行。

因此,输出将会是:

/*
    script start
    setTimeout
    script end
    Promise 1
    Promise 2
*/

三、Promise进阶

1、执行多个Promise对象 .all() 方法

.all 方法是并行执行多个Promise对象的静态方法,并在所有Promise对象都变为fulfilled时,返回一个新的Promise对象。新Promise对象的决议值是一个包含所有Promise对象决议值的数组。

示例如下:

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3]).then((results) => {
	console.log(JSON.stringify(results)); // [1, 2, 3]
});

2、与 all 类似的 allSettled 方法

不同的是,无论Promise对象的状态如何(fulfilled或rejected),都会返回一个新的Promise对象。新Promise对象的决议值也是一个包含所有Promise对象状态的对象数组。

示例代码:

const promise1 = Promise.resolve(1);
const promise2 = Promise.reject(new Error('error'));

Promise.allSettled([promise1, promise2]).then((results) => {
	console.log(JSON.stringify(results)); 
	// [
	//   { status: 'fulfilled', value: 1 },
	//   { status: 'rejected', reason: Error: error }
	// ]
});

3、有一个Promise对象的状态发生变化就返回的race方法

并行执行多个Promise对象,只要有一个Promise对象的状态发生变化(fulfilled或rejected),就会返回一个新的Promise对象。新的Promise对象状态和决议值与第一个发生变化的Promise对象相同。

示例如下:

// 定义一个函数,用于模拟网络请求
function fetchData(url, delay) {
	return new Promise((resolve, reject) => {
	// 使用 setTimeout 模拟网络请求的延迟
	setTimeout(() => {
	    fetch(url)
	        .then(response => response.text())
	        .then(data => resolve(data))
	        .catch(error => reject(error));
	    }, delay);
	});
}

// 创建两个 Promise,分别代表不同的网络请求
const promise1 = fetchData('https://www.baidu.com', 500);
const promise2 = fetchData('https://baijiahao.baidu.com/s?id=1823678166030721916', 100);

// 使用 Promise.race 来竞争这两个 Promise
Promise.race([promise1, promise2]).then((value) => {
	console.log('Won the race:', value); // 这里是promise2 
}).catch((error) => {
	console.log('An error occurred:', error);
});

4、与 all 相反的 any 方法

只要有一个Promise对象的状态变为fulfilled,就会返回一个新的Promise对象。新Promise对象的状态为fulfilled,决议值为第一个变为fulfilled的Promise对象的决议值。

示例如下:

const promise1 = Promise.reject(new Error('error1'));
const promise2 = Promise.reject(new Error('error2'));
const promise3 = Promise.resolve('success');

Promise.any([promise1, promise2, promise3]).then((value) => {
	console.log(value); // 'success'
}).catch((error) => {
	// 所有 Promise 都被 rejected 时才会执行
	console.log('所有异步操作都失败了');
});

5、无论怎样都执行 finally 方法

用于指定无论Promise对象的状态如何(fulfilled或rejected),都会执行的操作。

示例如下:

promise.finally(() => {
	console.log('异步操作完成,执行清理工作');
});

四、总结

Promise对象是JavaScript中处理异步操作的重要工具,它提供了统一的API,使得各种异步操作都可以用同样的方法进行处理。通过Promise对象,我们可以更方便地处理异步操作的结果和错误,实现更加复杂和灵活的异步编程逻辑。而通过理解它和微任务队列的关系,可以更好地预测和控制 JavaScript 代码中异步操作的执行顺序。

注意:

  • Promise对象一旦创建并开始执行,就无法中途取消。
  • 若不设置回调函数,Promise内部抛出的错误不会反应到外部。
  • 当Promise对象处于pending状态时,无法得知目前的进展是刚开始还是即将完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackispy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值