Promise

目录

    • 什么是Promise
    • Promise 和它的工作机制
    • 使用XHR和Promise封装模拟axios

什么是Promise

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

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

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

Promise_第1张图片

注意:每个 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_第2张图片

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()
    }
  })
}

你可能感兴趣的:(前端,javascript,前端,开发语言)