Promise

发布时间:2024年01月06日

什么是Promise

Promise 是 JavaScript 中用于处理异步操作的对象。它代表了一个承诺,表示一个异步操作的最终完成结果(成功或失败),并可以使用链式的方式处理这个结果。

Promise 对象可以有以下三个状态:

  1. 等待态(pending):初始状态,异步操作还未完成,既没有被兑现,也没有被拒绝。
  2. 完成态(fulfilled):异步操作成功完成。
  3. 拒绝态(rejected):异步操作失败。

在这里插入图片描述

注意:每个 Promise 对象一旦被兑现/拒绝,那就是已敲定了,状态无法再被改变

一个 Promise 对象在状态发生改变时,会触发相应的状态处理函数。这些处理函数包括:

  • then(onFulfilled, onRejected): 注册一个处理成功完成的回调函数(onFulfilled)和一个处理失败的回调函数(onRejected)。
  • catch(onRejected): 注册一个处理失败的回调函数。
  • finally(onFinally): 注册一个无论成功还是失败都会执行的回调函数。

Promise 管理异步任务,语法怎么用?

// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {
 // 2. 执行异步任务-并传递结果
 // 成功调用: resolve(值) 触发 then() 执行
 // 失败调用: reject(值) 触发 catch() 执行
})
// 3. 接收结果
p.then(result => {
 // 成功
}).catch(error => {
 // 失败
})

以下是一个 Promise 对象的示例:

const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const randomNumber = Math.random();
    if (randomNumber < 0.5) {
      resolve(randomNumber);
    } else {
      reject(new Error('操作失败'));
    }
  }, 1000);
});

promise.then((result) => {
  console.log('成功:', result);
}).catch((error) => {
  console.error('失败:', error);
}).finally(() => {
  console.log('无论成功或失败都会执行');
});

在这个示例中,Promise 构造函数接收一个带有两个参数的回调函数,这两个参数分别是 resolvereject。当异步操作成功完成时,调用 resolve 并传递结果;当异步操作失败时,调用 reject 并传递一个错误对象。

通过调用 then 方法,我们可以注册一个处理成功完成的回调函数,catch 方法可以注册一个处理失败的回调函数,而 finally 方法可以注册一个无论成功还是失败都会执行的回调函数。

使用 Promise 的好处是它可以处理异步操作的结果,而不是用回调函数嵌套回调函数,使代码更清晰、更易读。此外,Promise 还支持链式调用,允许在不同的异步操作之间进行流畅的操作,避免了回调地狱的问题。

除了手动创建 Promise 对象,许多现代的 JavaScript 异步操作也会返回一个 Promise 对象,如浏览器的 Fetch API、使用 fetch 进行网络请求、使用 axios 进行 Ajax 请求等都是基于 Promise 的。这让异步程序更加易于编写和组织。
在这里插入图片描述

Promise 和它的工作机制

让我们更深入地了解 Promise 和它的工作机制。

Promise 构造函数

Promise 是一个构造函数,用于生成一个 Promise 实例。构造函数接收一个执行器函数作为参数,这个执行器函数又接收两个函数作为参数:resolvereject

const promise = new Promise((resolve, reject) => {
  // 异步操作代码
});
  • resolve: 当异步操作成功时,你应该调用 resolve 函数,并传入值作为 Promise 的结果。
  • reject: 当异步操作失败时,你应该调用 reject 函数,并传入一个 Error 对象或其他错误信息作为 Promise 的失败原因。

Promise 状态转换

一个 Promise 的状态一旦从 pending 改变为 fulfilledrejected,它就不能再次改变状态。resolve 函数会将 Promise 的状态从 pending 设置为 fulfilled,而 reject 会将状态设置为 rejected

Promise.prototype.then()

then 方法接收两个可选参数,分别对应状态变为 fulfilledrejected 时的回调函数。

// 成功和失败都注册回调函数
promise.then(
  value => { /* 处理成功 */ },
  error => { /* 处理失败 */ }
);

then 方法返回一个新的 Promise 实例,并不是原来的 Promise,这允许它进行链式调用。链式调用中,每一次 then 的返回都可以被下一个 then 接收。

Promise.prototype.catch()

catch 方法是 then(null, onRejected) 的语法糖,用于注册当 Promise 状态变为 rejected 时的回调函数。

promise.catch(
  error => { /* 处理失败 */ }
);

同样地,catch 返回一个新的 Promise 实例,可以接着进行链式调用。

Promise.prototype.finally()

finally 方法注册一个回调函数,无论 Promise 的状态如何,这个回调函数都会被执行。

promise.finally(() => {
  // 不论成功或失败,都会执行的代码
});

finally 通常用于清理操作,如关闭文件流或数据库连接等。

Promise 静态方法

  • Promise.resolve: 可以将一个值快速转换为一个状态为 fulfilledPromise
const promise = Promise.resolve(42);
  • Promise.reject: 可以将一个给定的理由转换为一个状态为 rejectedPromise
const promise = Promise.reject(new Error('出错了'));
  • Promise.all: 该方法接收一个 Promise 数组作为输入,并返回一个新的 Promise 实例。这个新的 Promise 只有在所有输入的 Promise 都成功完成时才会成功(状态为 fulfilled)。如果有任意一个输入的 Promise 失败,新的 Promise 会立即失败(状态为 rejected)。
Promise.all([promise1, promise2]).then(results => {
  // 所有 promise 成功解决时的处理
}).catch(error => {
  // 任意一个 promise 失败时的处理
});
  • Promise.race: 同样接收一个 Promise 数组作为输入。但它返回的新 Promise 实例将采用数组中第一个改变状态的 Promise 的结果作为它的结果。
Promise.race([promise1, promise2]).then(result => {
  // 第一个解决的 promise 的处理
}).catch(error => {
  // 第一个失败的 promise 的处理
});
  • Promise.allSettled: 它返回一个在所有给定的 Promise 已被解决或被拒绝后的 Promise,并带有一个对象数组,每个对象表示对应的 Promise 的结果。

  • Promise.any: 可以等待多个 Promise 的其中一个成功完成,返回第一个成功完成的 Promise 的结果。如果所有 Promise 都失败了,返回一个聚合错误。

错误处理

Promise 执行器中抛出一个异常时,这个异常会被捕获,并使得 Promise 失败,即状态变为 rejected。这个异常会传递给 catch 方法或 then 方法中注册的失败回调函数。

new Promise((resolve, reject) => {
  throw new Error('出错了');
}).catch(error => {
  console.error(error); // '出错了'
});

链式调用

Promise 支持链式调用,允许我们进行更复杂的异步任务序列化处理。每个 thencatch 返回一个新的 Promise,可以通过 thencatch 链接下一个处理过程。

fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    console.log(data);
    return data; // 返回的数据可以再次被下一个 .then 使用
  })
  .catch(error => {
    console.error('Error fetching data:', error);
  })
  .finally(() => {
    console.log('Clean up actions go here');
  });

每次 thencatch 方法将返回一个新的 Promise 实例,可以继续链接更多的 thencatch 方法或直至 finally 方法。这样允许你构建承诺的序列,一步接一步处理异步操作及其结果。

使用XHR和Promise封装模拟axios

简易封装

/**
 * 目标:封装_简易axios函数
 *  1. 定义myAxios函数,接收配置对象,返回Promise对象
 *  2. 发起XHR请求,默认请求方法为GET
 *  3. 调用成功/失败的处理程序
*/
// 1. 定义myAxios函数,接收配置对象,返回Promise对象
function myAxios(config) {
  return new Promise((resolve, reject) => {
    // 2. 发起XHR请求,默认请求方法为GET
    const xhr = new XMLHttpRequest()
    xhr.open(config.method || 'GET', config.url)
    xhr.addEventListener('loadend', () => {
      // 3. 调用成功/失败的处理程序
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.response))
      } else {
        reject(new Error(xhr.response))
      }
    })
    xhr.send()
  })
}

如果携带请求参数

function myAxios(config) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    // 1. 判断有params选项,携带查询参数
    if (config.params) {
      // 2. 使用URLSearchParams转换,并携带到url上
      const paramsObj = new URLSearchParams(config.params)
      const queryString = paramsObj.toString()
      // 把查询参数字符串,拼接在url?后面
      config.url += `?${queryString}`
    }

    xhr.open(config.method || 'GET', config.url)
    xhr.addEventListener('loadend', () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.response))
      } else {
        reject(new Error(xhr.response))
      }
    })
    xhr.send()
  })
}

修改 myAxios 函数支持传递请求体数据

function myAxios(config) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()

    if (config.params) {
      const paramsObj = new URLSearchParams(config.params)
      const queryString = paramsObj.toString()
      config.url += `?${queryString}`
    }
    xhr.open(config.method || 'GET', config.url)

    xhr.addEventListener('loadend', () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.response))
      } else {
        reject(new Error(xhr.response))
      }
    })
    // 1. 判断有data选项,携带请求体
    if (config.data) {
      // 2. 转换数据类型,在send中发送
      const jsonStr = JSON.stringify(config.data)
      xhr.setRequestHeader('Content-Type', 'application/json')
      xhr.send(jsonStr)
    } else {
      // 如果没有请求体数据,正常的发起请求
      xhr.send()
    }
  })
}
文章来源:https://blog.csdn.net/m0_62943934/article/details/135429485
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。