✨ 专栏介绍
在现代Web开发中,JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性,还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言,JavaScript具有广泛的应用场景,并且不断发展演进。在本专栏中,我们将深入学习JavaScript语言的基本语法、DOM操作、事件处理、异步编程以及常见算法和数据结构等内容。此外,我们还将介绍ES6及其后续版本中引入的新特性,如箭头函数、模块化、解构赋值等。通过学习这些内容,你将能够成为一名熟练的JavaScript开发者,并能够应用这些知识来构建出高质量和可维护的Web应用程序。让我们一起开始JavaScript之旅吧!
Promise
是一种用于处理异步操作的机制,它可以将异步操作的结果以同步的方式进行处理和返回。在JavaScript
中,Promise
是一种内置对象,但我们也可以手动实现一个Promise
类来更好地理解其原理和工作方式。
Promise的特性
首先,让我们来介绍一下:
- Promise有三种状态:
pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。初始状态为pending
,当异步操作完成时,可以变为fulfilled
或rejected
。 - Promise具有链式调用的特性。通过then方法可以注册回调函数,在异步操作完成后执行这些回调函数。
then
方法返回一个新的Promise
对象,使得多个异步操作可以按顺序执行。 - Promise可以通过
resolve
方法将状态从pending
变为fulfilled
,并传递一个值作为成功的结果;也可以通过reject方法将状态从pending变为rejected,并传递一个原因作为失败的结果。 - Promise可以通过
catch
方法捕获错误,并处理错误情况。 - Promise还提供了一些静态方法,如
resolve
、reject
、all
和race
等。其中,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)
}
}
}
- 在
resolve
函数中,我们首先判断promise的当前状态是否为PENDING
。如果是,那么将状态改为FULFILLED
并将返回值赋给value属性,并依次调用成功回调数组中的回调函数。 - 在
reject
函数中,同样首先判断promise
的当前状态是否为PENDING
。如果是,那么将状态改为REJECTED
并将错误原因赋给reason
属性,并依次调用失败回调数组中的回调函数。 - 在构造函数的末尾,通过
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
- catch方法是then方法的一个语法糖,用于捕获promise链中的错误。它接受一个参数onRejected,表示失败的回调函数,然后调用then方法,并将onRejected作为第二个参数传入。
- 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专栏