likes
comments
collection
share

Promise:使命必达!!!

作者站长头像
站长
· 阅读数 30

前言

如果一个函数的执行结果,需要另一个函数的执行结果,而这一个函数的执行结果如果还需要另一个函数的执行结果呢?如果一直嵌套下去呢?你可能会说怎么可能会有这么恶心的需求出现,但实际上这在网页开发的过程中是完全有可能的,也是开发者必须要面对的问题,例如:假设我们要从一个API获取用户信息,然后基于用户ID从另一个API获取用户的订单列表,最后根据订单中的商品ID获取商品详情。

在JS的Es6版本之前,由于js是单线程语言,要模拟这样一个异步的需求就需要用到回调函数,将函数传入另一个函数调用,使这个函数能准确获取这个用户的ID信息来计算结果,然后把这个函数传入下一个函数调用,使下一个函数能获取到这个函数的执行结果进行调用....如此循环往复,一步步的嵌套不仅使代码变得晦涩难懂、不优雅,还会使得维护的成本过高,想想也知道,要是其中哪一个函数计算出错,就会导致最后的结果南辕北辙。我们称这种用回调函数不断嵌套下去来模拟异步需求的行为为回调地狱

在JS2015年的ES6版本发布后,许多像这样为程序员所诟病的问题得到了解决,其中就有今天的主角Promise方法

基础知识

要了解Promise的执行执行机制,首先要来了解一下JS处理异步任务的核心机制---Event Loop事件循环

Event Loop

Event Loop(事件循环)是JavaScript引擎处理异步操作和事件的一种机制,它允许JavaScript这样的单线程语言在执行过程中能够处理并发,而不会阻塞主线程。尽管JavaScript主要在浏览器和Node.js环境中运行,但Event Loop的概念对于理解这两种环境中的异步行为至关重要。

基本原理

  1. 单线程执行:JavaScript是单线程的,这意味着同一时间只能执行一个任务。对于UI渲染和响应用户交互来说,这是有益的,因为它避免了多线程环境中的数据竞争和同步问题。

2 任务队列:Event Loop的核心思想是将任务分为两类:宏任务(Macro Task)微任务(Micro Task) 。宏任务包括setTimeout、setInterval、I/O、UI渲染等;微任务包括Promise的回调、process.nextTick(Node.js特有)、MutaionObserver等。每当有任务完成,其回调会被放入相应的任务队列。

3 执行流程

-   JavaScript引擎首先执行全局脚本代码,这是当前的宏任务。
-   执行过程中,遇到异步操作时,将其回调函数安排到相应的任务队列。
-   当当前宏任务执行完毕,Event Loop会检查微任务队列,如果有待处理的微任务,则全部执行完这些微任务。
-   清空微任务队列后,执行下一个宏任务,如此循环往复。

Node.js中的Event Loop阶段

在Node.js中,Event Loop被细分为多个阶段,每个阶段处理特定类型的回调。这些阶段按照特定顺序循环执行,直到没有可执行的任务。主要阶段包括:

  1. Timers:处理setTimeout和setInterval设定的回调。
  2. Pending Callbacks:处理一些延迟到下一轮循环的I/O回调。
  3. Idle, Prepare:内部处理,与Node.js的内部操作相关。
  4. Poll:检查新的I/O事件,执行对应的回调;如果没有其他任务,会在此阶段阻塞等待。
  5. Check:执行setImmediate的回调。
  6. Close Callbacks:处理关闭请求的回调,如socket.on('close', ...)。

重要概念

  • 异步非阻塞:Event Loop确保即使有长时间运行的异步操作(如文件读写、网络请求),也不会阻塞主线程,因此UI可以保持响应。
  • 微任务优先:相比于宏任务,微任务总是在当前宏任务结束后立即执行,这使得微任务适合于需要快速响应的场景。

了解了JS的异步处理机制就可以来看一下Promise了。

Promise

Promise是JS的一种处理异步操作的方法,它提供了一种更优雅、更易于管理的方式来处理异步代码,而不是传统的回调函数。Promise代表一个异步操作的最终完成(或失败)及其结果值。它有三种状态:

  1. pending(等待中) :初始状态,既没有完成也没有失败。
  2. fulfilled(已成功) :表示操作成功完成,此时Promise变为 resolved,并带有成功的结果值。
  3. rejected(已失败) :表示操作失败,此时Promise变为 rejected,并可能带有错误原因。

基本用法

创建一个Promise实例通常使用new Promise((resolve, reject) => {...})构造函数,其中resolvereject是两个函数,分别用于改变Promise的状态。

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('操作成功');
    } else {
      reject('操作失败');
    }
  }, 2000);
});

// 使用then处理成功情况,catch处理失败情况
myPromise
  .then(result => console.log(result))
  .catch(error => console.error(error));

链式调用

Promise支持链式调用,即.then()方法可以返回一个新的Promise,使得多个异步操作可以按顺序执行。

Javascript
myPromise
  .then(result => {
    console.log(result);
    return new Promise((resolve) => {
      setTimeout(() => resolve('第二个操作成功'), 1000);
    });
  })
  .then(secondResult => console.log(secondResult))
  .catch(error => console.error(error));

Promise.all()

当需要并行执行多个Promise并等待它们全部完成时,可以使用Promise.all()方法。

const promise1 = Promise.resolve('Promise 1');
const promise2 = new Promise((resolve) => setTimeout(resolve, 500, 'Promise 2'));
const promise3 = new Promise((resolve, reject) => setTimeout(reject, 900, 'Promise 3 failed'));

Promise.all([promise1, promise2, promise3])
  .then(results => console.log(results)) // 不会被调用,因为其中一个Promise被reject了
  .catch(error => console.error(error)); // 输出: "Promise 3 failed"

Promise.race()

如果只需要等待其中任何一个Promise完成(无论成功还是失败),可以使用Promise.race()方法。

Javascript
Promise.race([promise1, promise2, promise3])
  .then(result => console.log(result)) // 可能是任何一个先完成的Promise的结果
  .catch(error => console.error(error)); // 如果第一个完成的是rejected的Promise,则会捕获到错误

作为与Promise紧密相连的.then()方法,而它的使用也很关键。

then()

在JavaScript编程环境里,then 是与 Promise 对象紧密相关的概念。

基本用法

当你创建一个 Promise 实例时,通常会传入一个 executor 函数,该函数接收两个参数:resolvereject,分别用于标记 Promise 成功(fulfilled)或失败(rejected)。

一旦 Promise 的状态从 pending(等待中)变为 fulfilled 或 rejected,其关联的 then 方法就会被调用。then 方法接受两个参数:第一个是处理成功情况的回调函数(onFulfilled),第二个是处理失败情况的回调函数(onRejected),这两个参数都是可选的。

示例

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("操作成功");
  }, 1000);
});

promise.then(
  (result) => {
    console.log(result); // 输出: "操作成功"
  },
  (error) => {
    console.error(error);
  }
);

链式调用

then 方法的真正强大之处在于它可以链式调用,使得多个异步操作可以按顺序执行,每个 then 的返回值可以传递给下一个 then

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("第一步完成");
  }, 1000);
});

promise
  .then((result) => {
    console.log(result); // 输出: "第一步完成"
    return "第二步完成";
  })
  .then((nextResult) => {
    console.log(nextResult); // 输出: "第二步完成"
  })
  .catch((error) => {
    console.error(error);
  });

错误处理

catch 方法用于捕获整个Promise链中的错误,它等同于在每个 then 中添加错误处理回调,但更简洁且集中处理错误。

promise
  .then(...)
  .then(...)
  .catch((error) => {
    console.error(error);
  });

结语

这就是JS的Es6版本更新的Promise的具体用法,这个方法解决了为人诟病的回调地狱,与其他一些好用优雅的方法一起组成的Es6版本,也让前端向前跨进了一大步。

如果我的分享对你有用,不妨点个赞吧,你的赞对我很重要!!!

我是Ace,我们下次再见!

转载自:https://juejin.cn/post/7392541545659547683
评论
请登录