小白的JS学习之路(七)—— “Promise”

3 阅读7分钟

小白的JS学习之路(七)—— “Promise”

学习笔记整理,记录我从回调函数到Promise的学习过程,如果有理解错误的地方,欢迎大佬指正!

前言

学完了闭包和原型,接下来遇到了JavaScript的另一个"大BOSS"——异步编程和Promise

说实话,刚开始学异步的时候,我是一头雾水的:为什么我的代码不按顺序执行?为什么数据获取不到?Promise、then、resolve、reject……这些概念之间到底是什么关系?


一、我是怎么理解"异步"的?

1.1 从操作系统课说起

上周上操作系统课,老师讲了进程的四大特性:虚拟、异步、并发、共享

我突然联想到JS里的异步编程!老师说的并发性:在同一时间间隔内,执行多个任务。这不就是JS里异步编程的核心思想吗?

1.2 举个栗子🌰

我先试着写了个简单的例子:

javascript复制

let a = 1
setTimeout(() => {
    a = 2
}, 1000)
console.log(a)  // 输出:1(不是2!)

我的执行过程理解

  1. let a = 1 是同步任务,马上执行
  2. setTimeout 是异步任务,JS引擎说:“好的,我知道了,1秒后再来执行这个”
  3. 但是!JS不会等着,而是继续执行后面的 console.log(a)
  4. 所以输出的是 1,而不是 2

💡 我的通俗理解:就像食堂打饭,前面的人还在挑菜(耗时任务),你可以先去拿餐具(不耗时的任务),不用傻等着。

1.3 JS为什么是单线程?

我查了资料才知道:

  • JS运行在V8引擎
  • V8引擎默认只开启一个线程来执行代码
  • 所以遇到耗时任务,只能先挂起,等主线程空闲再回来执行

我的类比

  • 浏览器新开一个tab页 = 开启一个新进程
  • 这个进程里有多个线程通力合作(网络线程、渲染线程、JS执行线程等)
  • 但是!JS执行只有一个线程,所以遇到耗时操作需要"异步"来处理

二、回调函数:我第一次遇到的坑

2.1 什么是回调?

我在MDN上看到的定义是:“把函数作为参数传递”。

但我更喜欢这样理解:当B函数需要依赖异步的A函数结果时,我们把B函数的调用放在A函数里面

javascript复制

let a = null

function A() {
    setTimeout(() => {
        a = 100  // 1秒后才赋值
        B()      // 在回调里调用 B
    }, 1000)
}

function B() {
    console.log(a) // 只有当 A 完成后,才能正确输出 100
}

A()

我的问题:这相当于"强行把异步捋顺成同步",虽然能解决问题,但代码写得很丑 😑

2.2 回调地狱(Callback Hell)

当我尝试用回调写复杂逻辑时,代码变成了这样:

javascript复制

// 假设我们要依次完成:相亲 → 结婚 → 生娃
xq(function() {
    marry(function() {
        baby(function() {
            // 还能再嵌套吗?能!但代码已经没法看了
        })
    })
})

我的崩溃瞬间

  • ❌ 代码像楼梯一样往右歪,看着眼晕
  • ❌ 报错时找不到是哪个环节出问题
  • ❌ 想改逻辑?抱歉,请做好重构的准备

三、Promise:我的救星!🌟

3.1 Promise是什么?

我在MDN上看到的定义是:“Promise 是一个表示异步操作最终完成或失败的对象。”

我的理解:Promise就像一个"承诺",它有三种状态:

  • pending:待定状态(正在进行中)
  • fulfilled:已成功状态(承诺兑现)
  • rejected:已失败状态(承诺失败)

🎯 生活例子:就像你向女神表白,女神说"我考虑一下"(pending),要么"好的"(fulfilled),要么"你是个好人"(rejected)。

3.2 我的第一个Promise代码

我照着MDN的示例,写了第一个Promise:

javascript复制

function xq() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('刘局相亲成功')
            resolve()  // 通知后续:我成功了!
        }, 2000)
    })
}

function marry() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('刘局结婚了')
            resolve()
        }, 1000)
    })
}

function baby() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('刘局生了孩子')
            resolve()
        }, 1000)
    })
}

我的理解

  • new Promise() 创建一个Promise对象
  • resolve 是"兑现承诺"的函数
  • reject 是"拒绝承诺"的函数
  • 在异步操作成功后调用 resolve()

3.3 Promise链式调用(解决回调地狱)

我以前的写法(回调地狱)

javascript复制

xq(function() {
    marry(function() {
        baby()
    })
})

现在用Promise(链式调用)

javascript复制

xq()
    .then(() => {
        return marry()  // 重要:一定要return!
    })
    .then(() => {
        baby()
    })
    .catch(() => {
        console.log('刘局相亲失败')
    })

输出顺序

code复制

刘局相亲成功
(等待2秒)
刘局结婚了
(等待1秒)
刘局生了孩子

我的感受

  • ✅ 代码像同步一样从上往下读,太爽了!
  • ✅ 错误处理集中(catch捕获所有失败)
  • ✅ 可维护性强,想加逻辑直接加 .then()

四、深入理解:我尝试手写Promise

为了真正理解Promise,我尝试写了一个简版:

javascript复制

function Promise(fn) {
    this.state = 'pending'  // 初始状态:待定
    this.arr = []           // 存储 then 中的回调函数

    const resolve = (res) => {
        this.state = 'resolved'  // 状态变为:成功
        // 执行所有存起来的回调函数
        this.arr.forEach(fn => fn(res))
    }

    const reject = () => {
        this.state = 'rejected'
    }

    fn(resolve, reject)  // 执行传入的函数
}

我的理解

  1. new Promise() 得到一个状态为 pending 的对象
  2. .then(foo) 会把 foo 存到 this.arr 数组里
  3. 当 resolve() 被调用时,Promise状态改为成功,并执行所有存起来的回调函数

💡 我的感悟:原来Promise的核心就是"状态管理 + 回调函数队列"啊!


五、实战中遇到的坑(我的血泪史)

坑1:忘记return!

javascript复制

// ❌ 我的错误写法
xq().then(() => {
    marry()  // 没有 return,链式调用会断!
})

// ✅ 正确写法
xq().then(() => {
    return marry()  // 返回 Promise,链式调用继续
})

我的教训:不return的话,下一个 .then() 拿不到Promise对象,代码就断开了!

坑2:错误处理

javascript复制

// 方法1:catch捕获(推荐)
xq()
    .then(() => marry())
    .then(() => baby())
    .catch((err) => {
        console.log('出错了:', err)
    })

// 方法2:then的第二个参数
xq()
    .then(() => marry(), (err) => {
        console.log('出错了:', err)
    })

我的选择:推荐用 .catch(),因为它能捕获整个链式调用中的所有错误,更清晰。

坑3:Promise.all vs Promise.race

javascript复制

// Promise.all:所有都成功才成功
Promise.all([xq(), marry(), baby()])
    .then(() => console.log('全部完成!'))

// Promise.race:谁快用谁
Promise.race([xq(), marry(), baby()])
    .then(() => console.log('有一个完成了!'))

我的理解

  • Promise.all:等所有人都到齐才开会(全部成功)
  • Promise.race:谁跑得快谁赢(第一个完成的)

六、我的学习总结

方式我的评价适用场景
回调函数简单但难维护简单异步操作
Promise链式调用太爽了大多数异步场景
async/await还没学,据说更爽复杂异步逻辑

我的学习路径

  1. 先搞懂异步和回调的问题(踩坑)
  2. 学会Promise的基本用法(then/catch)
  3. 多写例子,体会链式调用的优雅
  4. 尝试手写简版Promise,理解原理
  5. 下一步:学async/await!

写在最后

Promise是前端异步编程的基石,虽然我一开始也很难理解,但多写多练就能掌握。

我的学习资源

  • ✅ MDN Web Docs - Promise(强烈推荐!官方文档最权威)
  • ✅ 阮一峰《ES6 标准入门》(写得通俗易懂)
  • ✅ 各位大佬的掘金文章(感谢分享!)

我的建议(仅供参考):

  • 不要死记硬背,要理解原理
  • 多写例子,从简单到复杂
  • 遇到问题先查MDN,再查掘金文章
  • 尝试手写简版,加深理解

最后:这篇文章是我个人的学习笔记,如果有理解错误的地方,欢迎大佬指正!我们一起进步 💪


参考资料

  • MDN Web Docs - Promise
  • 阮一峰《ES6 标准入门》
  • 各位大佬的掘金分享

关键词:#JavaScript #ES6 #Promise #异步编程 #小白学习笔记

PS:如果觉得这篇文章有帮助,欢迎点赞收藏!有问题也可以在评论区留言,我会尽量回复(虽然我也是小白哈哈 😊)


🔗 系列文章

  • 小白的JS学习之路(三)—— JS类型
  • 小白的JS学习之路(四)—— 闭包
  • 小白的JS学习之路(五)—— 原型
  • 小白的JS学习之路(六)—— this
  • 小白的JS学习之路(七)—— Promise(本文)