JavaScript 知识量:26 - 101 - 483
期约是异步编程的一种机制,用于表示一个正在处理但尚未结束的操作。当相关操作结束时,期约会通知系统。在 ECMAScript 中,期约机制是实现异步编程的重要组成部分,从 ES6 开始,新增了 Promise 类型来实现期约。
Promise 是一个有状态的对象,它可能处于以下三种状态之一:
待定(pending)
兑现(fulfilled,或被称为“解决”,resolved)
拒绝(rejected)
在期约的状态转换中,从 pending 状态可以转换为 resolved 或 rejected,分别代表了成功和失败的状态。但无论转换为何种状态,都是不可逆的。当 Promise 的状态转换为 resolved 时,会有一个私有的内部值(value),而转换为 rejected 时,会有一个私有的内部理由(reason),默认值为 undefined。由于期约的状态是私有的,所以改变其状态只能在内部操作。
Promises/A+ 是 JavaScript 中的一个规范,它是一种异步编程的方式,用于处理可能成功也可能失败的操作。这个规范是由 JavaScript 社区的一些主要成员(包括 jQuery、Node.js 和 RSVP.js 等)共同开发的,其目标是创建一种通用的、易于实现和互操作的 Promise API。
Promises/A+ 规范的核心是两个关键点:
1. Promise API:这个 API 定义了 Promise 对象的基本行为,包括两个主要方法:then 和 catch。
then:这个方法接收两个参数,第一个是成功时的回调函数,第二个是失败时的回调函数。它返回一个新的 Promise 对象,允许链式调用。
catch:这个方法用于指定一个失败时的回调函数。它返回一个新的 Promise 对象,即使前面的操作成功,这个回调函数也会被执行。
2. 遵循规范:实现 Promise/A+ 的库或框架必须遵循这个规范定义的行为和 API。这保证了使用 Promises/A+ 的代码可以在不同的库或框架之间互操作。
以下是一个 Promises/A+ 的简单示例:
var promise = new Promise(function(resolve, reject) { // 一些异步操作 setTimeout(function() { if (/* 操作成功 */) { resolve('成功的结果'); } else { reject('失败的原因'); } }, 1000); }); promise.then(function(result) { // 成功时的回调函数 console.log(result); }, function(reason) { // 失败时的回调函数 console.log(reason); });
Promises/A+ 规范有助于编写简洁、可读性强的异步代码,并允许开发者在不同的环境中灵活地使用 Promise API。
以下是如何创建并使用 Promise 的一个基本示例:
// 创建一个新的 Promise let promise = new Promise(function(resolve, reject) { // 这是一个异步操作,比如请求数据 // 当操作成功时,调用 resolve() 并传递结果 // 如果失败,调用 reject() 并传递错误 setTimeout(function() { if (/* 操作成功 */) { resolve('成功的结果'); } else { reject('失败的原因'); } }, 1000); }); // 使用 .then 和 .catch 处理 Promise 的结果 promise.then(function(result) { // 这会在 Promise 完成并返回结果时运行 console.log(result); // 打印 "成功的结果" }).catch(function(reason) { // 这会在 Promise 失败并返回错误时运行 console.log(reason); // 打印 "失败的原因" });
在这个示例中,创建了一个新的 Promise,该 Promise 在一秒后决定。它模拟了一个异步操作(如网络请求)。如果该操作成功,调用 resolve 并传入结果;如果失败,调用 reject 并传入错误。然后使用 .then 和 .catch 来处理 Promise 的结果。如果 Promise 成功完成,会在 .then 中打印结果。如果 Promise 失败,会在 .catch 中打印错误。
期约连锁是一种处理多个异步操作的方式,其中每个操作依赖于前一个操作的完成。它使用Promise对象和async/await语法来实现。
期约连锁允许将多个异步操作串联起来,每个操作在前面的操作完成后执行。这类似于一系列的异步函数调用,但是更加简洁和易于管理。
下面是一个简单的示例,演示了如何在JavaScript中使用期约连锁:
// 定义异步函数 function asyncFunction1() { return new Promise(resolve => { setTimeout(() => { console.log('Async function 1'); resolve(); }, 1000); }); } function asyncFunction2() { return new Promise(resolve => { setTimeout(() => { console.log('Async function 2'); resolve(); }, 1000); }); } function asyncFunction3() { return new Promise(resolve => { setTimeout(() => { console.log('Async function 3'); resolve(); }, 1000); }); } // 使用期约连锁执行异步函数 asyncFunction1() .then(asyncFunction2) .then(asyncFunction3) .then(() => { console.log('All async functions completed'); });
在上面的示例中,定义了三个异步函数asyncFunction1,asyncFunction2和asyncFunction3。每个函数都返回一个Promise对象,并在1秒钟后打印一个消息并解析Promise。然后,使用期约连锁将这三个异步函数连接起来,依次执行。最后一个.then()中的函数会在所有异步函数完成后执行,打印出“All async functions completed”消息。
可以根据需要添加更多的异步函数到期约链中,并使用.then()来指定每个函数完成后要执行的操作。这种模式可以轻松地管理多个异步操作,并确保它们按照预期的顺序执行。
期约合成通常指的是将多个异步操作组合到一起,等待所有操作都完成后再进行下一步操作。可以使用Promise对象来进行期约合成。下面是一个简单的示例:
// 假设有两个异步函数需要执行 function asyncFunction1() { return new Promise(resolve => { setTimeout(() => { console.log('Async function 1'); resolve(); }, 1000); }); } function asyncFunction2() { return new Promise(resolve => { setTimeout(() => { console.log('Async function 2'); resolve(); }, 1000); }); } // 使用Promise.all进行期约合成 Promise.all([asyncFunction1(), asyncFunction2()]) .then(() => { console.log('All async functions completed'); });
在上面的示例中,定义了两个异步函数asyncFunction1和asyncFunction2,它们都返回一个Promise对象。然后,使用Promise.all()将这两个异步函数组合到一起,等待它们都完成后执行下一步操作。在Promise.all()中,传递了一个包含两个异步函数的数组,这个数组中的所有Promise对象都完成后,才会执行.then()中的回调函数。这个回调函数会在所有异步函数都完成后打印出"All async functions completed"消息。
在JavaScript中,可以使用Promise.race()方法和Promise.all()方法来取消期约。
Promise.race()方法接受一个Promise对象的数组作为参数,并返回一个新的Promise对象。这个新的Promise对象会在数组中的任意一个Promise对象改变状态时改变自己的状态,并采用相应的状态值。因此,如果想要取消期约,只需要将所有的Promise对象放入一个数组中,然后把这个数组传递给Promise.race()方法。如果在数组中的任意一个Promise对象改变了状态,那么这个新的Promise对象就会完成,取消了期约。
下面是一个使用Promise.race()方法取消期约的示例代码:
const promise1 = new Promise((resolve, reject) => { setTimeout(resolve, 500); }); const promise2 = new Promise((resolve, reject) => { setTimeout(resolve, 1000); }); const promises = [promise1, promise2]; const cancelledPromise = Promise.race(promises); cancelledPromise.then(() => { console.log('At least one promise in the array resolved'); }).catch((error) => { console.error('Error:', error); }); // The promise from cancelledPromise has been cancelled promises.forEach((promise) => { promise.catch((error) => { console.error('Error:', error); }); });
在上面的代码中,定义了两个Promise对象promise1和promise2,它们分别会在500毫秒和1000毫秒后完成。将这两个Promise对象放入一个数组promises中,然后将这个数组传递给Promise.race()方法,得到一个新的Promise对象cancelledPromise。如果在promise1和promise2中的任意一个Promise对象完成之前,调用了cancelledPromise.catch()方法,那么这个新的Promise对象就会立即被拒绝,并返回相应的错误信息。在调用cancelledPromise.catch()方法之后,还可以通过调用每个原始Promise对象的.catch()方法来捕获它们被取消时抛出的错误信息。
与Promise.race()方法不同的是,Promise.all()方法会将所有的Promise对象都完成后才会改变自己的状态。因此,要取消一个Promise.all()的期约,就需要手动将所有的Promise对象都取消。可以使用Promise.all()方法返回的Promise对象的.cancel()方法来取消所有的Promise对象。这个方法会返回一个布尔值,表示是否成功取消了所有的Promise对象。如果成功取消了所有的Promise对象,那么这个Promise对象就会立即被拒绝,并返回相应的错误信息。下面是一个使用Promise.all()方法取消期约的示例代码:
const promise1 = new Promise((resolve, reject) => { setTimeout(resolve, 500); }); const promise2 = new Promise((resolve, reject) => { setTimeout(resolve, 1000); }); const promises = [promise1, promise2]; const cancelledPromise = Promise.all(promises); cancelledPromise.then(() => { console.log('All promises in the array resolved'); }).catch((error) => { console.error('Error:', error); }); // Cancel all promises in the array and reject the promise from cancelledPromise promises.forEach((promise) => { promise.cancel(); });
在上面的代码中,定义了两个Promise对象promise1和promise2,它们分别会在500毫秒和1000毫秒后完成。将这两个Promise对象放入一个数组promises中,然后将这个数组传递给Promise.all()方法,得到一个新的Promise对象cancelledPromise。如果在promise1和promise2中的所有Promise对象完成之前,调用了每个原始Promise对象的.cancel()方法来取消它们,那么这个新的Promise对象就会立即被拒绝,并返回相应的错误信息。
ECMAScript 6期约并不支持进度追踪,但是可以通过扩展来实现。可以通过以下方式实现 Promise 的进度通知:
function promiseWithProgress(promise, onProgress) { return new Promise((resolve, reject) => { const intervalId = setInterval(() => { if (promise.percentage) { onProgress(promise.percentage); } }, 1000); // 每秒发送一次进度通知 promise.then(result => { clearInterval(intervalId); // 清除定时器 resolve(result); }).catch(error => { clearInterval(intervalId); // 清除定时器 reject(error); }); }); } // 使用示例 const slowPromise = new Promise((resolve) => { setTimeout(() => { resolve('Slow promise resolved'); }, 5000); // 模拟一个耗时5秒的异步操作 }); promiseWithProgress(slowPromise, (progress) => { console.log(`Progress: ${progress}`); // 周期性的打印进度 }).then(() => { console.log('Promise resolved'); // Promise 完成时打印的信息 });
在这个例子中,promiseWithProgress 函数接收一个 Promise 对象和一个 onProgress 回调函数作为参数。这个函数会返回一个新的 Promise 对象,当原始 Promise 对象执行过程中有进度更新时,就会调用 onProgress 回调函数,并将进度值作为参数传递给它。在 onProgress 回调函数中,可以周期性的输出或更新进度信息。
Copyright © 2017-Now pnotes.cn. All Rights Reserved.
编程学习笔记 保留所有权利
MARK:3.0.0.20240214.P35
From 2017.2.6