深入浅出Promise,循序渐进掌握JavaScript异步编程

2023-11-26 12:09:43 浏览数 (1)

一. Promise基本用法

Promise 是 JavaScript 中处理异步操作的一种方式。它是一个对象,代表了一个异步操作的最终完成或失败的结果。

Promise 有三种状态: pending (进行中)fulfilled (已成功) 和 rejected (已失败)。一旦 Promise 的状态变为 fulfilled 或 rejected ,就称为 resolved (已解决)。在 resolved 状态下, Promise 的结果值就被确定了。

Promise的基本用法如下:

代码语言:javascript复制
const promise = new Promise((resolve, reject) => {
  // 异步操作...
  if (/* 异步操作成功 */) {
    resolve(result); // 将结果传递给resolve函数
  } else {
    reject(error); // 将错误信息传递给reject函数
  }
});

promise
  .then(result => {
    // 处理异步操作成功的结果
  })
  .catch(error => {
    // 处理异步操作失败的结果
  });

在上面的示例中,我们创建了一个 Promise 对象,并在构造函数中传入一个执行器函数(executor function)。执行器函数接受两个参数, resolve 和 reject 函数,用于将 Promise 的状态改变为 fulfilled 或 rejected 。

执行器函数中进行异步操作,当异步操作成功时,调用resolve函数传递结果值;当异步操作失败时,调用reject函数传递错误信息。

接着,我们通过调用 Promise 的then方法来设置异步操作成功时的回调函数,并通过catch方法来设置异步操作失败时的回调函数。then方法可以链式调用,每个then方法都返回一个新的 Promise 实例,因此可以实现连续的异步操作。

除了thencatch方法外, Promise 还提供了一些其他的方法,如finally方法、Promise.allPromise.race等,用于处理更复杂的异步操作场景。

需要注意的是, Promise 的状态一旦改变就不会再改变。因此,即使异步操作完成后再次调用 resolve 或 reject 函数,也不会对 Promise 的状态产生影响。

二. Promise的高级用法

除了以上介绍基本的用法外, Promise 还提供了一些高级的用法,下面介绍几个常用的高级用法:

1.Promise.all: Promise.all方法接收一个由 Promise 实例组成的数组作为参数,并返回一个新的 Promise 实例。该新的Promise实例在数组中的所有 Promise 实例都变为fulfilled状态后,才会变为fulfilled状态,并将每个 Promise 实例的结果值组成一个数组传递给回调函数。

代码语言:javascript复制
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

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

2.Promise.race: Promise.race方法同样接收一个由 Promise 实例组成的数组作为参数,并返回一个新的 Promise 实例。该新的 Promise 实例在数组中的第一个 Promise 实例变为fulfilledrejected状态后,即变为对应的状态,并将第一个 Promise 实例的结果(或错误信息)传递给回调函数。

代码语言:javascript复制
const promise1 = new Promise((resolve) => {
  setTimeout(() => resolve('Promise 1'), 2000);
});

const promise2 = new Promise((resolve) => {
  setTimeout(() => resolve('Promise 2'), 1000);
});

Promise.race([promise1, promise2])
  .then(result => {
    console.log(result); // Promise 2
  });

3.Promise.resolve: Promise.resolve方法返回一个新的 Promise 实例,该实例的状态为fulfilled,并将传递的值作为结果。

4.Promise.rejectPromise.reject方法返回一个新的Promise实例,该实例的状态为rejected,并将传递的值作为错误信息。

代码语言:javascript复制
const promise1 = Promise.resolve('resolved');
promise1.then(result => {
  console.log(result); // resolved
});

const promise2 = Promise.reject(new Error('rejected'));
promise2.catch(error => {
  console.error(error); // Error: rejected
});

5.Promise.prototype.finally: Promise.prototype.finally方法用于指定不管 Promise 状态如何,都会执行的回调函数。该方法返回一个新的 Promise 实例,它在回调函数执行完毕后,根据之前 Promise 实例的状态,变为对应的状态。

代码语言:javascript复制
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('done');
  }, 1000);
});

promise
  .then(result => {
    console.log(result); // done
  })
  .catch(error => {
    console.error(error);
  })
  .finally(() => {
    console.log('finally'); // finally
  });

除了上述介绍的方法外,Promise还提供了很多其他的方法,如Promise.allSettledPromise.any等,用于处理更复杂的异步操作场景。

三. Promise的异步编程场景

以下是一些Promise的异步编程场景的例子:

1.发起网络请求:当需要从服务器获取数据时,可以使用 Promise 来发起异步网络请求。通过使用 Promise 封装XMLHttpRequestfetch API,我们可以在请求完成后,通过then方法处理返回的数据或错误信息。

代码语言:javascript复制
function getData(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    
    xhr.onload = function() {
      if (xhr.status === 200) {
        resolve(xhr.responseText);
      } else {
        reject(new Error(xhr.statusText));
      }
    };
    
    xhr.onerror = function() {
      reject(new Error('Network Error'));
    };
    
    xhr.send();
  });
}

getData('https://api.example.com/data')
  .then(response => {
    console.log('Data:', response);
  })
  .catch(error => {
    console.error('Error:',);
  });

2.并行执行多个异步操作:当需要同时执行多个异步操作,并在所有操作都完成后进行处理时,可以使用Promise.all方法。Promise.all接受一个包含多个 Promise 对象的数组作为参数,并返回一个新的 Promise 对象,当所有Promise都解决(fulfilled)时,返回的 Promise 对象也将解决(fulfilled),并提供一个包含所有解决值的数组。

代码语言:javascript复制
const loadData = () => {
  const request1 = getData('https://api.example.com/data1');
  const request2 = getData('https://api.example.com/data2');
  const request3 = getData('https://api.example.com/data3');
  
  return Promise.all([request1, request2, request3]);
};

loadData()
  .then(dataArr => {
    console.log('Data 1:', dataArr[0]);
    console.log('Data 2:', dataArr[1]);
    console.log('Data 3 dataArr[2]);
   .catch(error => {
    console.error('Error:', error);
  }); 

3.异步操作的串执行:当需要按照顺序依次执行一系列异步操作,且每个操作依赖上一个操作的结果时,可以通过then方法的链式调用来实现。每个then方法中返回一个新的 Promise 对象,用于传递上一个操作的结果给下一个操作。

代码语言:javascript复制
getData('https://api.example.com/data1')
  .then(response1 => {
    console.log('Data 1:', response1);
    return getData('https://api.example.com/data2');
  })
  .then(response2 => {
    console.log('Data 2:', response2);
    return getData('https://api.example.com/data3');
  })
  .then(response3 => {
    console.log('Data 3:', response3);
  })
  .catch(error => {
    console.error('Error:', error);
  });

这些例子展示了 Promise 在异步编程中的一些应用场景,包括网络请求、并行执行和串行执行等。通过合理利用 Promise 的特性,我们可以实现更优雅、可读性更高的代码。

四. Promise的影响

Promise 的出现对JavaScript编程带来了以下几个重要的贡献:

  1. 处理异步操作JavaScript是单线程的,异步操作的处理一直是发者们头疼的问题。而 Promise 通过提供一种构化的方式来处理异步操作,避免了回调地狱(callback hell)的问题。 Promise 的链式调用使得异步操作可以按照顺序执行,提高了代码的可读性和可维性。
  2. 错误处理:传统的回调函数方式对错误处理较为繁琐,容易出现遗漏或混乱。而 Promise 通过catch方法提供了统一的错误处理机制,使得错误处理变得简洁明了。同时, Promise 还可以将同步代码和异步代码的错误处理方式统一起来,提高了的一致性。
  3. 并行操作: Promise 的些高级方法如Promise.allPromise.race,使得并行操作变得更加简单。开发者可以很方便地将多个异步操作并行执行,并等待它们全部完成或任一完成后继续进行后续处理。
  4. 更好的代码组织: Promise 的链式调用可以使代码逻辑更加清晰可读。通过将异步操作按照顺序连接起来,能够更好地组织,易于理解和维护。

总的来说, Promise 的出现使得JavaScript在处理异步操作方面变得更加简洁、可读、可维护,提高了开发效率和代码质量。它改变了JavaScript编程的方式,成为现代异步程的重要工具之一。

五. Promise实现的基本原理

Promise 的源码实现原理可以简要概括如下:

  1. 构造函数: Promise 是一个构造函数,当我们使用new关键字创建一个 Promise 对象时,会调用构造函数。构造函数接受一个executor函数作为参数,executor函数在 Promise 对象的实例化过程中立即执行,它接受两个参数:resolvereject
  2. 状态管理: Promise 对象有三个状态:pendingfulfilledrejected。初始状态为pending,执行executor函数时可以调用resolve函数将状态从pending转为fulfilled,或调用reject函数将状态从pending转为rejected。同时, Promise 对象还有一个内部属性value用于保存resolve函数传递的值,或reason来保存reject函数传递的错误信息。
  3. 回调函数: Promise 对象可以通过thencatchfinally等方法注册回调函数,处理异步操作的结果或错误信息。then方法用于注册成功的回调函数,catch方法用于注册失败的回调函数,finally方法则用于注册无论成功或失败都会被调用的回调函数。
  4. 异步操作: Promise 的实现中,可以通过setTimeoutsetImmediate等宏任务和微任务的方法进行异步操作的处理。在和reject函数被调用时,会根据状态的变化,将对应的回调函数添加到任务队列中,并在适当的时候执行。
  5. 链式调用:通过then方法的链式调用,可以将多个异步操作按顺序组织起来。当一个 Promise 对象的状态变为fulfilled时,会执行当前then方法的回调函数,并将回调函数的返回值作为下一个then方法的参数。

总的来说, Promise 的源码实现原理是通过构造函数实例化 Promise 对象,在对象中管理状态、回调函数和异步操作。通过thencatchfinally等方法来注册和执行回调函数,实现了异步操作的顺序控制和错误处理。具体实现会涉及到一些细节,例如任务队列的管理和错误处理的机制,这些都是 Promise 的实现细节。

六. Promise和SetTimeout的区别

Promise 和setTimeout在处理异步操作时有一些区别:

  1. 功能和用途: Promise 是一种用于处理异步操作的对象,它提供了一种更优雅和可靠的方式来处理异步操作的结果和错误。 Promise 可以用于处理异步操作的流程控制,以及实现依赖关系和顺序执行。而setTimeout是浏览器提供的一个函数,用于在指定的时间间隔后执行一次回调函数或代码。
  2. 结构和调用方式: Promise 是一个对象,它有自己的方法和状态。我们通过new关键字创建 Promise 实例,并通过thencatchfinally等方法来注册回调函数。而setTimeout是一个函数,我们可以直接调用它,传递回调函数和延时时间。
  3. 错误处理: Promise 提供了更完善的错误处理机制。我们可以通过注册catch方法来捕获并处理 Promise 中的错误信息。而setTimeout只能通过try-catch语句块来处理回调函数中可能发生的错误。
  4. 异步操作的控制和组织: Promise 允许我们通过串行地、并行地和异步地组织和控制异步操作的流程。通过使用then方法的链式调用,我们可以按照期望的次序执行异步操作,并处理它们的结果。而setTimeout只能用于延时执行一次回调函数,并没有提供更高级的流程控制和依赖管理。
  5. 可读性和可维护性: Promise 的代码往往更加可读、简洁和易于维护。通过链式调用的方式,我们可以将异步操作按照顺序组织起来,并在每一步都进行必要的处理。而setTimeout的代码往往需要通过回调函数的嵌套来处理多个异步操作,使代码变得复杂和难以理解。

综上所述, Promise 和setTimeout在处理异步操作时的功能、用途、结构和调用方式、错误处理、控制和组织方式等方面有一些区别, Promise 更加灵活强大,能够提供更好的异步编程体验。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞