JavaScript Promise 详解
什么是Promise?
Promise是JavaScript中处理异步操作的现代方式,它代表一个"将来会完成的操作"的结果。
生活中的类比
想象你在餐厅点餐:
下单:你点了一份菜(创建Promise)
等待:厨师在做菜,你不知道什么时候好(Pending状态)
结果:
菜做好了,服务员送来(Fulfilled状态)
厨师说没有这个菜了(Rejected状态)
Promise的三种状态
graph LR
A[Pending<br/>等待中] --> B[Fulfilled<br/>成功完成]
A --> C[Rejected<br/>失败/出错]
style A fill:#fff3cd
style B fill:#d4edda
style C fill:#f8d7da
// Promise的生命周期
const promise = new Promise((resolve, reject) => {
// 初始状态:Pending(等待中)
setTimeout(() => {
const success = Math.random() > 0.5
if (success) {
resolve("操作成功!") // 变为 Fulfilled
} else {
reject("操作失败!") // 变为 Rejected
}
}, 2000)
})
与Go语言的对比
Go语言的异步处理
package main
import (
"fmt"
"time"
)
// Go中使用goroutine + channel处理异步
func fetchDataGo() <-chan string {
resultChan := make(chan string, 1)
go func() {
// 模拟耗时操作
time.Sleep(2 * time.Second)
// 随机成功或失败
if time.Now().Unix()%2 == 0 {
resultChan <- "数据获取成功"
} else {
resultChan <- "ERROR: 获取失败"
}
close(resultChan)
}()
return resultChan
}
func main() {
fmt.Println("开始获取数据...")
// 阻塞等待结果
result := <-fetchDataGo()
fmt.Println("结果:", result)
fmt.Println("程序结束")
}
JavaScript的Promise处理
// JavaScript中使用Promise处理异步
function fetchDataJS() {
return new Promise((resolve, reject) => {
// 模拟耗时操作
setTimeout(() => {
// 随机成功或失败
if (Math.random() > 0.5) {
resolve("数据获取成功")
} else {
reject("ERROR: 获取失败")
}
}, 2000)
})
}
console.log("开始获取数据...")
// 非阻塞处理结果
fetchDataJS()
.then(result => console.log("结果:", result))
.catch(error => console.log("错误:", error))
console.log("程序继续执行...") // 立即执行,不等待
Promise的基本语法
1. 创建Promise
const myPromise = new Promise((resolve, reject) => {
// executor函数:立即执行的代码
// 异步操作成功时调用resolve
// 异步操作失败时调用reject
})
2. 使用Promise
myPromise
.then(result => {
// 处理成功结果
console.log("成功:", result)
})
.catch(error => {
// 处理错误
console.log("失败:", error)
})
.finally(() => {
// 无论成功失败都会执行
console.log("操作完成")
})
实际应用示例
1. 网络请求模拟
// 模拟API调用
function fetchUserInfo(userId) {
return new Promise((resolve, reject) => {
console.log(`正在获取用户${userId}的信息...`)
// 模拟网络延迟
setTimeout(() => {
if (userId <= 0) {
reject(new Error("用户ID无效"))
return
}
// 模拟网络失败
if (Math.random() < 0.2) {
reject(new Error("网络连接失败"))
return
}
// 成功返回用户数据
const userData = {
id: userId,
name: `用户${userId}`,
email: `user${userId}@example.com`,
skills: ['Go', 'PHP', 'JavaScript']
}
resolve(userData)
}, 1500)
})
}
// 使用示例
fetchUserInfo(123)
.then(user => {
console.log("获取到用户信息:", user)
console.log(`欢迎 ${user.name}!`)
return user.skills // 可以返回值给下一个then
})
.then(skills => {
console.log("用户技能:", skills.join(', '))
})
.catch(error => {
console.error("获取用户信息失败:", error.message)
})
.finally(() => {
console.log("用户信息请求处理完成")
})
2. 文件操作模拟
// 模拟文件读取
function readFile(filename) {
return new Promise((resolve, reject) => {
console.log(`正在读取文件: ${filename}`)
setTimeout(() => {
// 模拟文件不存在
if (filename.includes('notfound')) {
reject(new Error(`文件不存在: ${filename}`))
return
}
// 模拟文件内容
const content = `这是 ${filename} 的内容\n包含一些重要数据...`
resolve(content)
}, 1000)
})
}
// 使用示例
readFile('config.json')
.then(content => {
console.log("文件内容:")
console.log(content)
})
.catch(error => {
console.error("读取文件失败:", error.message)
})
Promise链式调用
// 多个异步操作按顺序执行
function step1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("步骤1完成")
resolve("step1 result")
}, 1000)
})
}
function step2(data) {
return new Promise(resolve => {
setTimeout(() => {
console.log("步骤2完成, 接收到:", data)
resolve("step2 result")
}, 1000)
})
}
function step3(data) {
return new Promise(resolve => {
setTimeout(() => {
console.log("步骤3完成, 接收到:", data)
resolve("final result")
}, 1000)
})
}
// 链式调用
step1()
.then(result1 => step2(result1))
.then(result2 => step3(result2))
.then(finalResult => {
console.log("所有步骤完成:", finalResult)
})
.catch(error => {
console.error("某个步骤失败:", error)
})
Promise的实用方法
1. Promise.all() - 并发执行
// 同时执行多个Promise,全部成功才返回
const promise1 = fetchUserInfo(1)
const promise2 = fetchUserInfo(2)
const promise3 = fetchUserInfo(3)
Promise.all([promise1, promise2, promise3])
.then(users => {
console.log("所有用户信息获取成功:")
users.forEach(user => console.log(user.name))
})
.catch(error => {
console.error("至少有一个用户信息获取失败:", error)
})
2. Promise.allSettled() - 获取所有结果
// 等待所有Promise完成,无论成功失败
Promise.allSettled([promise1, promise2, promise3])
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`用户${index + 1}:`, result.value.name)
} else {
console.log(`用户${index + 1}失败:`, result.reason.message)
}
})
})
3. Promise.race() - 竞速执行
// 返回最快完成的Promise结果
const fastPromise = new Promise(resolve => setTimeout(() => resolve("快"), 100))
const slowPromise = new Promise(resolve => setTimeout(() => resolve("慢"), 1000))
Promise.race([fastPromise, slowPromise])
.then(result => {
console.log("最快的结果:", result) // "快"
})
async/await - Promise的现代语法
传统Promise语法
function fetchAndProcessUser() {
return fetchUserInfo(123)
.then(user => {
console.log("用户:", user.name)
return readFile(`${user.id}.txt`)
})
.then(fileContent => {
console.log("文件内容:", fileContent)
return "处理完成"
})
.catch(error => {
console.error("处理失败:", error)
throw error
})
}
async/await语法
async function fetchAndProcessUser() {
try {
const user = await fetchUserInfo(123)
console.log("用户:", user.name)
const fileContent = await readFile(`${user.id}.txt`)
console.log("文件内容:", fileContent)
return "处理完成"
} catch (error) {
console.error("处理失败:", error)
throw error
}
}
在Vue中的应用
<template>
<div>
<button @click="loadUser" :disabled="loading">
{{ loading ? '加载中...' : '获取用户信息' }}
</button>
<div v-if="user">
<h3>{{ user.name }}</h3>
<p>邮箱: {{ user.email }}</p>
<p>技能: {{ user.skills.join(', ') }}</p>
</div>
<div v-if="error" class="error">
错误: {{ error }}
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const user = ref(null)
const loading = ref(false)
const error = ref('')
const loadUser = async () => {
loading.value = true
error.value = ''
user.value = null
try {
// 使用我们之前定义的fetchUserInfo函数
const userData = await fetchUserInfo(123)
user.value = userData
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
</script>
<style>
.error {
color: red;
margin-top: 10px;
}
</style>
常见错误和最佳实践
❌ 常见错误
// 错误1: 忘记返回Promise
function badExample() {
fetchUserInfo(123).then(user => {
console.log(user)
// 没有返回Promise,外部无法等待
})
}
// 错误2: 不处理错误
fetchUserInfo(123)
.then(user => console.log(user))
// 没有.catch(),错误会被忽略
// 错误3: Promise中使用同步错误处理
new Promise((resolve, reject) => {
try {
// 异步操作
setTimeout(() => {
throw new Error("异步错误") // 这个错误不会被catch捕获
}, 1000)
} catch (error) {
reject(error) // 只能捕获同步错误
}
})
✅ 最佳实践
// 正确1: 总是返回Promise
function goodExample() {
return fetchUserInfo(123)
.then(user => {
console.log(user)
return user // 返回结果供后续使用
})
}
// 正确2: 始终处理错误
fetchUserInfo(123)
.then(user => console.log(user))
.catch(error => console.error("处理错误:", error))
// 正确3: 异步操作中正确处理错误
function createAsyncPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5
if (success) {
resolve("成功")
} else {
reject(new Error("异步操作失败")) // 正确的错误处理
}
}, 1000)
})
}
总结
Promise是JavaScript异步编程的核心概念,它:
解决回调地狱:让异步代码更加清晰
统一错误处理:通过.catch()统一处理错误
支持链式调用:可以优雅地组合多个异步操作
现代语法支持:与async/await配合使用体验更好
在Vue开发中,Promise无处不在:
网络请求(fetch、axios)
路由切换
组件懒加载
用户交互响应
掌握Promise是现代JavaScript开发的必备技能!
练习建议
运行本文档中的代码示例
修改示例中的参数,观察不同结果
尝试组合多个Promise,理解链式调用
用async/await重写Promise代码,体会语法差异
在Vue项目中实践,结合实际应用场景
记住:Promise不难,关键是要多练习和实际应用!