手写Promise完整介绍

✨ 专栏介绍

在现代Web开发中,JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性,还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言,JavaScript具有广泛的应用场景,并且不断发展演进。在本专栏中,我们将深入学习JavaScript语言的基本语法、DOM操作、事件处理、异步编程以及常见算法和数据结构等内容。此外,我们还将介绍ES6及其后续版本中引入的新特性,如箭头函数、模块化、解构赋值等。通过学习这些内容,你将能够成为一名熟练的JavaScript开发者,并能够应用这些知识来构建出高质量和可维护的Web应用程序。让我们一起开始JavaScript之旅吧!

在这里插入图片描述


Promise是一种用于处理异步操作的机制,它可以将异步操作的结果以同步的方式进行处理和返回。在JavaScript中,Promise是一种内置对象,但我们也可以手动实现一个Promise类来更好地理解其原理和工作方式。

在这里插入图片描述

Promise的特性

首先,让我们来介绍一下:

  1. Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。初始状态为pending,当异步操作完成时,可以变为fulfilledrejected
  2. Promise具有链式调用的特性。通过then方法可以注册回调函数,在异步操作完成后执行这些回调函数。then方法返回一个新的Promise对象,使得多个异步操作可以按顺序执行。
  3. Promise可以通过resolve方法将状态从pending变为fulfilled,并传递一个值作为成功的结果;也可以通过reject方法将状态从pending变为rejected,并传递一个原因作为失败的结果。
  4. Promise可以通过catch方法捕获错误,并处理错误情况。
  5. Promise还提供了一些静态方法,如resolverejectallrace等。其中,resolve方法返回一个已经成功的Promise对象;reject方法返回一个已经失败的Promise对象;all方法接收一个包含多个Promise对象的数组,并在所有Promise对象都成功时返回一个包含所有结果的新Promise对象;race方法接收一个包含多个Promise对象的数组,并在任意一个Promise对象成功或失败时返回相应结果。

接下来,我们从源码角度讲解一下手写的Promise类。

手写Promise

定义常量

首先,我们定义了三个常量:PENDING表示promise的初始状态,FULFILLED表示promise成功的状态,REJECTED表示promise失败的状态。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

定义MyPromise类

接下来,我们定义了一个MyPromise类,该类拥有以下属性:

  • state:表示promise的当前状态,默认为PENDING。
  • value:表示promise成功时的返回值,默认为undefined。
  • reason:表示promise失败时的错误原因,默认为undefined。
  • onFulfilledCallbacks:用于存储成功回调函数的数组。
  • onRejectedCallbacks:用于存储失败回调函数的数组。

构造函数(constructor)中接受一个executor函数作为参数,该函数接受两个参数:resolve和reject。我们在构造函数中定义了resolve和reject函数,并将它们传递给executor函数。如果executor函数执行成功,则调用resolve函数,如果执行失败,则调用reject函数。

class MyPromise {
  constructor(executor) {
    this.state = PENDING
    this.value = undefined
    this.reason = undefined
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = (value) => {
      if (this.state = PENDING) {
        this.state = FULFILLED
        this.value = value
        this.onFulfilledCallbacks.forEach(fn => fn())
      }
    }

    const reject = (reason) => {
      if (this.state === PENDING) {
        this.state = REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }

    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
}
  1. resolve函数中,我们首先判断promise的当前状态是否为PENDING。如果是,那么将状态改为FULFILLED并将返回值赋给value属性,并依次调用成功回调数组中的回调函数。
  2. reject函数中,同样首先判断promise的当前状态是否为PENDING。如果是,那么将状态改为REJECTED并将错误原因赋给reason属性,并依次调用失败回调数组中的回调函数。
  3. 在构造函数的末尾,通过try-catch语句执行executor函数。如果执行过程中有错误抛出,那么调用reject函数将错误原因赋给reason属性。

resolve和reject

接下来是resolve和reject方法的实现。resolve方法将状态从pending变为fulfilled,并传递一个值作为成功的结果;reject方法将状态从pending变为rejected,并传递一个原因作为失败的结果。

  static resolve(value) {
    return new MyPromise((resolve, reject) => {
      resolve(value)
    })
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }

then

  • 在then方法中,我们创建了一个新的MyPromise实例promise2,并在其构造函数中定义了resolve和reject函数。

  • 根据当前promise的状态,分别处理不同的情况:

    • 如果当前状态是PENDING,那么将onFulfilled和onRejected回调函数分别推入onFulfilledCallbacks和onRejectedCallbacks数组中。
    • 如果当前状态是FULFILLED,那么异步地执行onFulfilled回调,并根据返回值调用resolve或reject函数。
    • 如果当前状态是REJECTED,那么异步地执行onRejected回调,并根据返回值调用resolve或reject函数。
  • 最后,返回promise2实例。

then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') {
      onFulfilled = function (value) {
        return value
      }
    }

    if (typeof onRejected !== 'function') {
      onRejected = function (reason) {
        throw reason
      }
    }

    let promise2 = new MyPromise((resolve, reject) => {
      switch (this.state) {
        case PENDING:
          this.onFulfilledCallbacks.push(() => {
            setTimeout(() => {
              try {
                const value = onFulfilled(this.value)
                if (value instanceof MyPromise) {
                  value.then(resolve, reject)
                } else {
                  resolve(value)
                }
              } catch (err) {
                reject(err)
              }
            }, 0)
          })
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              try {
                const value = onRejected(this.reason)
                resolve(value)
              } catch (err) {
                reject(err)
              }
            }, 0)
          })
          break
        case FULFILLED:
          setTimeout(() => {
            try {
              const value = onFulfilled(this.value)
              resolve(value)
            } catch (err) {
              reject(err)
            }
          }, 0)
          break
        case REJECTED:
          setTimeout(() => {
            try {
              const value = onRejected(this.reason)
              resolve(value)
            } catch (err) {
              reject(err)
            }
          }, 0)
          break
      }
    })
    return promise2
  }

catch和finally

  1. catch方法是then方法的一个语法糖,用于捕获promise链中的错误。它接受一个参数onRejected,表示失败的回调函数,然后调用then方法,并将onRejected作为第二个参数传入。
  2. finally方法是then方法的另一个语法糖,无论promise的状态是成功还是失败,都会执行finally方法中的回调函数。它接受一个参数fn,并在then方法中通过两个回调函数分别调用fn,并根据返回值执行resolve或reject函数
  catch(onRejected) {
    return this.then(null, onRejected)
  }

  finally(fn) {
    return this.then(
      (value) => {
        fn()
        return value
      },
      (reason) => {
        fn()
        throw reason
      }
    )
  }
}

all和race

  • all方法接受一个promise数组作为参数,返回一个新的MyPromise实例。当所有 promise 都成功时,返回一个包含所有成功值的数组;否则,返回一个包含第一个失败的 promise 的错误原因的新的 MyPromise 实例。
  • race方法接受一个promise数组作为参数,返回一个新的MyPromise实例。当任何一个promise成功时,返回该成功的promise的值;否则,返回第一个失败的promise的错误原因的新的MyPromise实例。
  static all(promises) {
    return new Promise((resolve, reject) => {
      if (promises.length === 0) {
        resolve([])
      } else {
        let result = []
        let count = 0
        for (let i = 0;i < promises.length; i++) {
          promises[i].then(
            data => {
              result[i] = data
              count++
              if (count === promises.length) {
                resolve(result)
              }
            },
            err => {
              reject(err)
              return
            }
          )
        }
      }
    })
  }

  static race(promises) {
    return new Promise((resolve, reject) => {
      if (promises.length === 0) {
        resolve()
      } else {
        for (let i = 0; i < promises.length; i++) {
          promises[i].then(
            data => {
              resolve(data)
            },
            (err) => {
              reject(err)
              return
            }
          )
        }
      }
    })
  }

完整版MyPromise

const PENDING = 'pending'  // 声明一个常量PENDING,表示Promise的初始状态
const FULFILLED = 'fulfilled'  // 声明一个常量FULFILLED,表示Promise的成功状态
const REJECTED = 'rejected'  // 声明一个常量REJECTED,表示Promise的失败状态

class MyPromise {
  constructor(executor) {
    this.state = PENDING  // 初始化Promise的状态为PENDING
    this.value = undefined  // 初始化Promise的值为undefined
    this.reason = undefined  // 初始化Promise的失败原因为undefined
    this.onFulfilledCallbacks = []  // 存储Promise成功状态下的回调函数
    this.onRejectedCallbacks = []  // 存储Promise失败状态下的回调函数

    // 定义resolve函数,用于将Promise状态改为FULFILLED,并执行成功状态下的回调函数
    const resolve = (value) => {
      if (this.state = PENDING) {
        this.state = FULFILLED  // 将Promise状态改为FULFILLED
        this.value = value  // 存储Promise成功时的值
        this.onFulfilledCallbacks.forEach(fn => fn())  // 执行所有成功状态下的回调函数
      }
    }

    // 定义reject函数,用于将Promise状态改为REJECTED,并执行失败状态下的回调函数
    const reject = (reason) => {
      if (this.state === PENDING) {
        this.state = REJECTED  // 将Promise状态改为REJECTED
        this.reason = reason  // 存储Promise失败时的原因
        this.onRejectedCallbacks.forEach(fn => fn())  // 执行所有失败状态下的回调函数
      }
    }

    try {
      executor(resolve, reject)  // 执行executor函数,并传入resolve和reject参数
    } catch (err) {
      reject(err)  // 捕获错误,并将Promise状态改为REJECTED
    }
  }

  // 静态方法resolve,返回一个状态为FULFILLED的Promise实例
  static resolve(value) {
    return new MyPromise((resolve, reject) => {
      resolve(value)
    })
  }

  // 静态方法reject,返回一个状态为REJECTED的Promise实例
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }

  // 静态方法all,接收一个包含多个Promise实例的数组,返回一个新的Promise实例
  static all(promises) {
    return new Promise((resolve, reject) => {
      if (promises.length === 0) {
        resolve([])  // 如果传入的数组为空,则直接返回一个状态为FULFILLED的Promise实例
      } else {
        let result = []  // 存储每个Promise实例的执行结果
        let count = 0 // 计数器
        for (let i = 0;i < promises.length; i++) {
          promises[i].then(
            data => {
              result[i] = data  // 将每个Promise实例的执行结果存入result数组中
              count++
              if (count === promises.length) {
                resolve(result)  // 当所有Promise实例都执行完毕时,返回包含所有结果的新的Promise实例
              }
            },
            err => {
              reject(err)  // 如果其中一个Promise实例执行失败,则将新的Promise实例的状态改为REJECTED,并返回失败原因
              return
            }
          )
        }
      }
    })
  }

  // 静态方法race,接收一个包含多个Promise实例的数组,返回一个新的Promise实例
  static race(promises) {
    return new Promise((resolve, reject) => {
      if (promises.length === 0) {
        resolve()  // 如果传入的数组为空,则直接返回一个状态为FULFILLED的Promise实例
      } else {
        for (let i = 0; i < promises.length; i++) {
          promises[i].then(
            data => {
              resolve(data)  // 返回第一个执行完毕的Promise实例的结果
            },
            (err) => {
              reject(err)  // 如果其中一个Promise实例执行失败,则将新的Promise实例的状态改为REJECTED,并返回失败原因
              return
            }
          )
        }
      }
    })
  }

  // then方法,用于在Promise的成功和失败状态下执行回调函数,返回一个新的Promise实例
  then(onFulfilled, onRejected) {
    // 如果onFulfilled不是一个函数,则将其更改为返回接收到的值的函数
    if (typeof onFulfilled !== 'function') {
      onFulfilled = function (value) {
        return value
      }
    }

    // 如果onRejected不是一个函数,则将其更改为抛出接收到的原因的函数
    if (typeof onRejected !== 'function') {
      onRejected = function (reason) {
        throw reason
      }
    }

    let promise2 = new MyPromise((resolve, reject) => {
      switch (this.state) {
        // 如果Promise当前的状态是PENDING,则将回调函数添加到对应的回调数组中
        case PENDING:
          this.onFulfilledCallbacks.push(() => {
            setTimeout(() => {
              try {
                const value = onFulfilled(this.value)
                if (value instanceof MyPromise) {
                  value.then(resolve, reject)
                } else {
                  resolve(value)
                }
              } catch (err) {
                reject(err)
              }
            }, 0)
          })
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              try {
                const value = onRejected(this.reason)
                resolve(value)
              } catch (err) {
                reject(err)
              }
            }, 0)
          })
          break
        // 如果Promise当前的状态是FULFILLED,则直接执行成功回调函数
        case FULFILLED:
          setTimeout(() => {
            try {
              const value = onFulfilled(this.value)
              resolve(value)
            } catch (err) {
              reject(err)
            }
          }, 0)
          break
        // 如果Promise当前的状态是REJECTED,则直接执行失败回调函数
        case REJECTED:
          setTimeout(() => {
            try {
              const value = onRejected(this.reason)
              resolve(value)
            } catch (err) {
              reject(err)
            }
          }, 0)
          break
      }
    })
    return promise2
  }

  // catch方法,用于捕获Promise的失败状态,并执行回调函数
  catch(onRejected) {
    return this.then(null, onRejected)
  }

  // finally方法,无论Promise状态是成功还是失败,都会执行回调函数
  finally(fn) {
    return this.then(
      (value) => {
        fn()
        return value
      },
      (reason) => {
        fn()
        throw reason
      }
    )
  }
}

测试代码

复制完整代码加上下面的测试代码即可运行查看结果

const myPromise1 = MyPromise.resolve(1)

const myPromise2 = new MyPromise((resolve, reject) => {
	console.log(2)
	resolve(2)
}).then((res) => {
	console.log(3)
})

MyPromise.race([myPromise1, myPromise2]).then((res) => {
	console.log('result', res)
})

setTimeout(() => {
	const Promise1 = Promise.resolve(1)

	const Promise2 = new Promise((resolve, reject) => {
		console.log(2)
		resolve(2)
	}).then((res) => {
		console.log(3)
	})

	Promise.race([Promise1, Promise2]).then((res) => {
		console.log('result', res)
	})
}, 1000)


😶 写在结尾

JavaScript(ES6)专栏

在这里插入图片描述

JavaScript是一种广泛应用于网页开发和后端开发的脚本语言。它具有动态性、灵活性和易学性的特点,是构建现代Web应用程序的重要工具之一。在这个专栏中,我们将深入探讨JavaScript语言的基本语法、DOM操作、事件处理、异步编程以及常见算法和数据结构等内容。此外,我们还将介绍ES6(ECMAScript 2015)及其后续版本中引入的新特性,如箭头函数、模块化、解构赋值等。通过学习这些内容,你将能够成为一名熟练的JavaScript开发者,并能够应用这些知识来构建出高质量和可维护的Web应用程序。点击订阅JavaScript(ES6)专栏

前端设计模式专栏

在这里插入图片描述

设计模式是软件开发中不可或缺的一部分,它们帮助我们解决了许多常见问题,并提供了一种优雅而可靠的方式来构建应用程序。在本专栏中,我们介绍了所有的前端设计模式,包括观察者模式、单例模式、策略模式等等。通过学习这些设计模式,并将其应用于实际项目中,我们可以提高代码的可维护性、可扩展性和可重用性。希望这个专栏能够帮助你在前端开发中更好地应用设计模式,写出高质量的代码。点击订阅前端设计模式专栏

Vue专栏

在这里插入图片描述

Vue.js是一款流行的JavaScript框架,用于构建用户界面。它采用了MVVM(Model-View-ViewModel)的架构模式,通过数据驱动和组件化的方式,使开发者能够更轻松地构建交互性强、可复用的Web应用程序。在这个专栏中,我们将深入探讨Vue.js的核心概念、组件开发、状态管理、路由和性能优化等方面的知识。我们将学习如何使用Vue.js构建响应式的用户界面,并探索其强大的生态系统,如Vue Router和Vuex、Pinia。通过学习这些内容,你将能够成为一名熟练的Vue.js开发者,并能够应用这些知识来构建复杂而高效的Web应用程序。点击订阅Vue专栏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/271833.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一文看懂所有字符编码标准

文章目录 ASCII128个符号不够用欧洲国家亚洲国家 GB2312GBKGB18030UnicodeUTF-8 QA最高位b7是什么意思?UTF-8和UTF-16的区别UTF-8和UTF-32的区别 ASCII ASCII&#xff08;American Standard Code for Information Interchange&#xff09;是一种字符编码标准&#xff0c;最初…

基于业务功能级别的流量控制

之前产品线上发生过若干次因为tomcat连接池被耗尽而导致宕机的故障&#xff0c;而具体根源原因则各不尽相同。有因为调用和被调用的服务申请相同的分布式锁而导致死锁的&#xff0c;有因为发送内部或外部的JMS消息发生堵塞的&#xff0c;有因为某个存在性能问题的接口被较多调用…

如何使用队列处理 API 速率限制

对于遇到速率限制的应用程序来说也是一个挑战&#xff0c;因为它需要“放慢速度”或暂停。这是一个典型的场景&#xff1a; 初始请求&#xff1a;当应用程序发起与 API 的通信时&#xff0c;它会请求特定的数据或功能。API 响应&#xff1a; API 处理请求并响应请求的信息或执…

linxu重启网络服务失败——Failed to start LSB: Bring up/down networking.

一、出现问题的场景 在虚拟机中的Linux系统启动后&#xff0c;发现没有网络&#xff0c;执行ifconfig 发现自己配置的ens33网卡没有启动。 接着&#xff0c;执行systemctl restart network 重启网络服务失败 查看网络状态&#xff0c;执行 systemctl status network 发现报错…

有什么好用的C/C++源代码混淆工具?

​ 有什么好用的C/C源代码混淆工具&#xff1f; 开始使用ipaguard 前言 iOS加固保护是直接针对ios ipa二进制文件的保护技术&#xff0c;可以对iOS APP中的可执行文件进行深度混淆、加密。使用任何工具都无法逆向、破解还原源文件。对APP进行完整性保护&#xff0c;防止应用…

圣诞树(动态效果)

一、运行效果 二、制作方法 1.复制代码到Dreamweaver或HBuilder或vscode中 2.点击运行---运行到浏览器---选择你要打开的浏览器 3.打开后会出现这个界面&#xff0c;前四个是固定音乐&#xff0c;最后一个是自主选择的音乐&#xff0c;你可以选择你电脑上的歌曲&#xff0c…

CCF-CSP真题《202309-2 坐标变换(其二)》思路+python,c++满分题解

想查看其他题的真题及题解的同学可以前往查看&#xff1a;CCF-CSP真题附题解大全 试题编号&#xff1a;202309-2试题名称&#xff1a;坐标变换&#xff08;其二&#xff09;时间限制&#xff1a;2.0s内存限制&#xff1a;512.0MB问题描述&#xff1a; 问题描述 对于平面直角坐标…

vue项目中使用axios发送http请求添加header自定义变量出现跨域问题

request代码片段&#xff1a; export const request (api,method,params {},config,responseType {} ) > {let apiToken localStorage.getItem("token");let headers {Authorization: ${apiToken},};if (config?.headers) {headers {...headers,...config…

java:4-7运算符优先级

运算符优先级 运算符有不同的优先级&#xff0c;所谓优先级就是表达式运算中的运算顺序。如右表&#xff0c;上一行运算符总优先于下一行。只有单目运算符&#xff08;第二行&#xff09;、赋值运算符&#xff08;倒数3行&#xff09;是从右向左运算的。一览表, 不要背&#x…

SecuSphere:一款功能强大的一站式高效DevSecOps安全框架

关于SecuSphere SecuSphere是一款功能强大的一站式高效DevSecOps解决方案&#xff0c;DevSecOps作为一个经过针对性设计的集中式平台&#xff0c;可以帮助广大研究人员管理和优化漏洞管理、CI/CD管道集成、安全评估和DevSecOps实践。 SecuSphere是一个功能全面的DevSecOps平台…

公司办公文件数据\资料防泄密软件系统——自动智能透明加密保护核心数据

天锐绿盾办公文件数据防泄密软件系统是一款自动智能透明加密保护核心数据的软件系统。 PC访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 以下是该系统的几个核心特点&#xff1a; 自动智能加密&#xff1a;系统采用自动…

K8s 源码剖析及debug实战(一):Minikube 安装及源码准备

文章目录 0. 引言1. 什么是 Minikube2. 安装 Minikube3. 下载 Go4. 下载 Goland5. 下载 K8s 源码6. 后续 0. 引言 欢迎关注本专栏&#xff0c;本专栏主要从 K8s 源码出发&#xff0c;深入理解 K8s 一些组件底层的代码逻辑&#xff0c;同时借助 debug Minikube 来进一步了解 K8…

HarmonyOS Watch状态变量监听

今天 我们要将 Watch装饰器 状态变量更改通知 那么 关键点 状态变量 就是 更改后页面会响应式更新的响应式数据 我们可以这样写 Entry Component struct Index {State Watch("setName") name:string "小猫猫";setName() {console.log("变量改变&q…

为什么 C 语言被广泛应用于嵌入式系统开发?

为什么 C 语言被广泛应用于嵌入式系统开发&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「C 语言的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&a…

超纯水抛光树脂:光伏行业新技术应用

在清洁能源的领域中&#xff0c;高效太阳能电池&#xff0c;尤其是单晶硅电池&#xff0c;正日益受到重视。这些电池不仅转换效率高&#xff0c;而且耐用性强。然而&#xff0c;它们的制造过程对水质有着极高的要求&#xff0c;这就引入了超纯水的重要性。那么&#xff0c;超纯…

一套UWB精准定位系统源码,java语言开发,基于UWB技术自主研发的高精度人员定位系统源码

一套UWB精准定位系统源码&#xff0c;基于UWB技术自主研发的室内外人员定位系统源码 随着经济的高速发展&#xff0c;现代制造业生产设备日益繁多&#xff0c;生产车间面积广阔&#xff0c;生产工人数量多&#xff0c;存在难以进行有效管理和不便实施全方位风险管控的难题。 人…

kubernetes(k8s) Yaml 文件详解

YAML格式&#xff1a;用于配置和管理&#xff0c;YAML是一种简洁的非标记性语言&#xff0c;内容格式人性化&#xff0c;较易读。 1、查看API 资源版本标签 kubectl api-versions 2、编写资源配置清单 kubectl create -f nginx-test.yaml --validatefalse 2.3 查看创建的po…

MySQL报错:1054 - Unknown column ‘xx‘ in ‘field list的解决方法

我在操作MySQL遇到1054报错&#xff0c;报错内容&#xff1a;1054 - Unknown column Cindy in field list&#xff0c;下面演示解决方法&#xff0c;非常简单。 根据箭头指示&#xff0c;Cindy对应的应该是VARCHAR文本数字类型&#xff0c;字符串要用引号&#xff0c;所以解决方…

与供应商合作:成功供应商管理的六种最佳实践

许多企业低估了他们对外部供应商的依赖程度&#xff0c;也小看了这些供应商关系所涉及的风险。本文将探索企业与外部供应商合作的六种最佳实践&#xff0c;利用它们创建有效的供应商管理流程&#xff0c;从而降低成本和风险&#xff0c;并提高盈利能力。 供应商管理为何重要&a…