JavaScript

JavaScript 知识量:26 - 101 - 483

10.2 期约><

什么是期约- 10.2.1 -

期约是异步编程的一种机制,用于表示一个正在处理但尚未结束的操作。当相关操作结束时,期约会通知系统。在 ECMAScript 中,期约机制是实现异步编程的重要组成部分,从 ES6 开始,新增了 Promise 类型来实现期约。

Promise 是一个有状态的对象,它可能处于以下三种状态之一:

  1. 待定(pending)

  2. 兑现(fulfilled,或被称为“解决”,resolved)

  3. 拒绝(rejected)

在期约的状态转换中,从 pending 状态可以转换为 resolved 或 rejected,分别代表了成功和失败的状态。但无论转换为何种状态,都是不可逆的。当 Promise 的状态转换为 resolved 时,会有一个私有的内部值(value),而转换为 rejected 时,会有一个私有的内部理由(reason),默认值为 undefined。由于期约的状态是私有的,所以改变其状态只能在内部操作。

Promises/A+规范- 10.2.2 -

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。

创建新期约- 10.2.3 -

以下是如何创建并使用 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 中打印错误。

期约连锁- 10.2.4 -

期约连锁是一种处理多个异步操作的方式,其中每个操作依赖于前一个操作的完成。它使用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()来指定每个函数完成后要执行的操作。这种模式可以轻松地管理多个异步操作,并确保它们按照预期的顺序执行。

期约合成- 10.2.5 -

期约合成通常指的是将多个异步操作组合到一起,等待所有操作都完成后再进行下一步操作。可以使用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"消息。

期约取消- 10.2.6 -

在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对象就会立即被拒绝,并返回相应的错误信息。

期约进度通知- 10.2.7 -

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 回调函数中,可以周期性的输出或更新进度信息。