Promise

promise优势

Promise 对象是一个代理对象,通俗点讲可以把它理解为一个容器。它的参数是个回调函数,该回调接收一个resolvereject函数,异步任务成功的结果绑定至resolve函数,失败的结果绑定至reject函数。

Promise 出现的核心背景是解决回调函数的固有弊端

对比 Promise 回调函数
可读性 嵌套层级深(回调地狱),结构混乱 链式调用 可读性强
错误处理 无统一处理入口,容易遗漏 统一通过 catch 捕获所有层级错误
流程控制 需嵌套多层,逻辑繁琐 可通过 Promise.all / allSettled / race 控制
状态管理 无状态 有明确三种状态(pending / fulfilled / rejected)

Promise状态

一个 Promise 对象具备三种状态

  • pending: Promise新创建时的初始状态,还未执行 reslove 和 reject 方法
	const pending_promise = new Promise((resolve, reject) => {
    })
    console.log(pending_promise)

在这里插入图片描述

  • resolved/fulfilled: Promise对象在回调函数中调用了resolve方法后的状态
    const resolve_promise = new Promise((resolve, reject) => {
      resolve('resolve_promise')
    })
    console.log(resolve_promise)

在这里插入图片描述

  • rejected: Promise对象在回调函数中调用了reject 方法后的状态
    const reject_promise = new Promise((resolve, reject) => {
      reject('reject_promise')
    })
    console.log(reject_promise)

在这里插入图片描述

  • Promise状态转换是不可逆的,一旦从pending 状态转换至resolved/rejected,就不能再回到pending状态。

Promise状态控制

Promise就是一个容器,将异步任务包裹在这个容器里即可,异步任务成功和失败的结果分别赋给给 resolve 函数和 reject函数

then

Promise.then () 方法接收两个可选参数,分别用于处理 Promise 成功和失败的情况:

promise.then(onFulfilled, onRejected)
  • onRejected 参数与单独使用 catch () 效果类似,但 catch () 更常用(更清晰)
// 只处理成功
promise.then(result => console.log('成功:', result))

// 同时处理成功和失败
promise.then(
  result => console.log('成功:', result),
  error => console.log('失败:', error)
)

// 链式调用
promise
  .then(result => result + 1)
  .then(result => console.log('最终结果:', result))
  .catch(error => console.log('任何环节出错:', error))
  • promise读取图像
        function loadImg(url) {
            return new Promise((resolve, reject) => {
                const img = document.createElement('img') 
                img.onload = () => {                     
                    resolve(img)
                }
                img.onerror = () => {
                    const err = new Error(`图片加载失败${url}`)
                    reject(err)
                }
                img.src = url
            })
        }
        // 正确的图片地址
		const url1 = 'https://profile.csdnimg.cn/4/8/2/3_qq_37987033'
		// 错误的图片地址
        const url2 = 'https://profile.csdnimgxx.cn/4/8/2/3_qq_37987033-error' 

使用 promise 的 then 方法能够获取状态为 resolved 时的对象,并进行后续处理。then方法接收的是一个函数,该函数参数为promise执行 resolve 时带过来的值,如果传入的是其它的东西,then方法不会触发。

	loadImg(url1).then(img => {
         console.log(img)               
    })

在这里插入图片描述

catch捕获异常

使用 promise 的 **catch** 方法能够获取状态为 **rejected** 时的错误并抛出

        loadImg(url2).catch((err) => {
            console.error(err)    
        })

在这里插入图片描述

链式调用

promise在使用时允许链式调用

  • 当前一个 promise 的状态为 resolved 时,会跳过 catch 进入下一个 then 方法;当前一个promise的状态为 rejected 时,会跳过 then 进入下一个 catch 方法

  • 在使用链式调用时,then 方法中需要一个返回值作为下个 then 的输入, return 值的 3 种情况:

情况 1:return 普通值(字符串、数字、对象等)→ 新 Promise 变为 fulfilled,后续 then 接收该普通值。
情况 2:return 一个全新的 Promise_A → 新 Promise 的状态由 Promise_A 决定,后续 then 等待其状态变化后执行。
情况 3:不 return 任何值 → 等同于 return undefined,新 Promise 变为 fulfilled,后续 then 接收 undefined。

  • 无论是then方法还是catch方法,只要它们执行正常,返回值都会变成 resolved 状态;若执行过程中出错,就会变成 rejected 状态
// 简单链式调用(贴合前端接口请求场景)
new Promise((resolve) => {
  resolve(1); // 初始Promise变为fulfilled,值为1
})
.then(res => {
  console.log(res); // 输出1
  return res + 1; // return普通值,新Promise值为2
})
.then(res => {
  console.log(res); // 输出2
  // return一个新Promise
  return new Promise((resolve) => {
    setTimeout(() => resolve(res + 1), 1000);
  });
})
.then(res => {
  console.log(res); // 1秒后输出3(等待前一个return的Promise完成)
  throw new Error("测试错误"); // 抛出错误,触发后续catch
})
.then(
  res => console.log("不会执行"), // 被跳过
  err => console.log("失败回调", err) // 可捕获错误,也可用catch
)
.catch(err => {
  console.log("最终错误捕获", err); // 若上面then未写失败回调,此处捕获
});

执行结果:
在这里插入图片描述

分析:

  • 在第三个 then 函数中,由于 throw new Error ,将promise的状态改为了 reject,在第四个 then 中,命中了异常场景的处理, 打印出:"失败回调 3 "
  • 更推荐使用catch统一捕获:
// 简单链式调用(贴合前端接口请求场景)
new Promise((resolve) => {
  resolve(1); // 初始Promise变为fulfilled,值为1
})
.then(res => {
  console.log(res); // 输出1
  return res + 1; // return普通值,新Promise值为2
})
.then(res => {
  console.log(res); // 输出2
  // return一个新Promise
  return new Promise((resolve) => {
    setTimeout(() => resolve(res + 1), 1000);
  });
})
.then(res => {
  console.log(res); // 1秒后输出3(等待前一个return的Promise完成)
  throw new Error("测试错误"); // 抛出错误,触发后续catch
})
.then(
  res => console.log("不会执行"), // 被跳过
  // err => console.log("失败回调", err) // 可捕获错误,也可用catch
)
.catch(err => {
  console.log("最终错误捕获", err); // 若上面then未写失败回调,此处捕获
});

在这里插入图片描述

Promise的异步时机

promise在 new 时就会执行同步方法, resolve包裹而成的 Promise 对象在执行 then 时才是异步的。

        const promise = new Promise((resolve, reject) => {
            console.log(1);
            resolve('resolve1');
            console.log(2);
            resolve('resolve2')  // 状态只能改变一次
            console.log(3);
        });

        // 异步
        promise.then((res) => {
            console.log(4,res); 
        });

        console.log(5);

在这里插入图片描述

Promise流程处理

	// 成功的promise
    const promise1 = new Promise((resolve, reject) => {
      resolve('promise1')
    })
	// 成功的promise
    const promise2 = new Promise((resolve, reject) => {
      resolve('promise2')
    })
	// 失败的promise
    const promise3 = new Promise((resolve, reject) => {
      reject('promise3')
    })

all

  • Promise.all 接收一个Promise对象数组,对象数组里所有的 promise 对象都成功的时候才会触发成功;只要有一个失败(rejected),立即返回该失败原因,其余 Promise 停止执行。

	// promise.all 全部成功
    Promise.all([promise1, promise2]).then(res => {
      console.log(res);
    });
    
	// promise.all promise3失败成功
    Promise.all([promise1, promise2, promise3]).then(res => {
      console.log(res);
    }).catch(err=>{
      console.error(err)
    })

结果:
在这里插入图片描述

allSettled

  • Promise.all 接收一个Promise对象数组,无论所有 Promise 是成功还是失败,都会等待所有操作全部完成,返回一个包含所有结果的数组(每个结果对象包含状态和对应值 / 错误)
 Promise.allSettled([promise1, promise2, promise3]).then((results) => {
        // 遍历结果,区分成功/失败
        results.forEach((result) => {
          if (result.status === "fulfilled") {
            console.log("成功:", result.value);
          } else {
            console.log("失败:", result.reason);
          }
        });
      });

运行结果:
在这里插入图片描述

race

  • Promise.race 接收一个Promise对象数组 race方法返回第一个执行完毕的 promise 的结果,该结果可以被then 或者 catch捕获,其余的promise会被忽略
 const promiseRace1 = new Promise((resolve, reject) => {
        setTimeout(() => {
           resolve("promiseRace1");
        }, 500);   
      });

      const promiseRace2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("promiseRace2");
        }, 600);   
      });

      const promiseRace3 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject("promiseRace3");
        }, 700);   
      });

      Promise.race([promiseRace1, promiseRace2, promiseRace3]).then((results) => {
        console.log("race成功:", results);
      }).catch((err) => {
        console.error("race失败:", err);
      });

执行结果:
在这里插入图片描述

使用场景

  • all / allSettled / race
方法 规则 catch处理
all 全部成功才返回,一个失败则终止 立即返回失败原因,其余停止
allSettled 无论成败,等待全部完成,返回所有结果 不终止,收集所有失败原因
race 最先完成的返回(无论成败) 最先失败则返回失败原因
  • 场景: 前端首屏渲染前,需同时请求「用户信息接口」和「首页列表接口」,两个接口必须全部成功,才能渲染页面
  • 方法: all
// 模拟接口请求(真实开发中替换为fetch/axios)
const getUserInfo = () => new Promise(resolve => {
  setTimeout(() => resolve({ id: 1, name: '张三' }), 1000); // 1秒后返回用户信息
});
const getHomeList = () => new Promise(resolve => {
  setTimeout(() => resolve([{ id: 101, title: '首页文章1' }]), 800); // 800毫秒返回列表
});

// 用Promise.all并行请求,全部成功才渲染
Promise.all([getUserInfo(), getHomeList()])
  .then(([userInfo, homeList]) => {
    console.log('页面初始化完成');
    console.log('用户信息:', userInfo);
    console.log('首页列表:', homeList);
    // 执行页面渲染逻辑(仅当两个接口都成功才执行)
  })
  .catch(err => {
    console.log('页面加载失败:', err); // 只要一个接口失败,直接提示失败
  });
  • 场景: 用户批量上传图片,无需等待所有图片上传成功,哪怕部分失败,也要继续上传其余图片,最终统计 “成功几张、失败几张”,反馈给用户
  • 方法: allSettled
// 模拟图片上传(随机成功/失败,模拟真实上传异常)
const uploadImg = (imgName) => new Promise((resolve, reject) => {
  setTimeout(() => {
    // 随机模拟成功/失败(真实开发中根据接口响应判断)
    Math.random() > 0.5 
      ? resolve(`${imgName} 上传成功`)
      : reject(`${imgName} 上传失败(网络异常)`);
  }, 1000);
});

// 批量上传3张图片,收集所有结果
Promise.allSettled([
  uploadImg('图片1.png'),
  uploadImg('图片2.png'),
  uploadImg('图片3.png')
])
.then(results => {
  let successCount = 0;
  let failCount = 0;
  // 遍历所有结果,统计成败
  results.forEach(result => {
    if (result.status === 'fulfilled') {
      console.log(result.value);
      successCount++;
    } else {
      console.log(result.reason);
      failCount++;
    }
  });
  // 反馈给用户
  console.log(`上传完成:成功${successCount}张,失败${failCount}`);
});

  • 场景: 避免接口长期无响应导致页面卡死,设置「5 秒超时时间」—— 若 5 秒内接口未响应,直接提示 “请求超时”;若 5 秒内接口返回,正常处理数据
  • 方法: race
// 模拟商品详情接口(随机模拟响应快慢)
const getGoodsDetail = () => new Promise(resolve => {
  // 随机响应时间(1-6秒),模拟接口慢响应场景
  const delay = Math.random() * 5000 + 1000;
  setTimeout(() => resolve({ id: 202, name: '手机', price: 3999 }), delay);
});

// 模拟超时提示(5秒后触发失败)
const timeoutTip = () => new Promise((_, reject) => {
  setTimeout(() => reject(new Error('请求超时,请重试!')), 5000);
});

// 用race实现超时控制:接口和超时,谁先完成返回谁
Promise.race([getGoodsDetail(), timeoutTip()])
  .then(goodsDetail => {
    console.log('商品详情:', goodsDetail);
    // 渲染商品详情页面
  })
  .catch(err => {
    console.log(err.message); // 要么超时,要么接口失败,均提示错误
    // 显示超时/失败提示,允许用户重新请求
  });

async、await

async / await 和Promise的关系

  • await需要被一个async函数包裹,后面跟一个promise对象,它们的存在消灭了异步回调
  • 执行 async 函数,返回的是 Promise 对象,如果不是,它会自动把返回值包裹成一个promise对象
  • await 相当于 Promise 的 then;它会暂停 async 函数的执行,直到 Promise 状态改变
  • try...catch 可以捕获异常,代替了Promise 的 catch

async / await 本身是Promise的语法糖,它让原本的链式调用变成了真正的同步写法。除此之外,async / await 可以使用 try/catch 捕获异常,这是它的一大优势。

async / await 使用

  • async/await 读取图像
        function loadImg(url) {
            return new Promise((resolve, reject) => {
                const img = document.createElement('img') 
                img.onload = () => {                     
                    resolve(img)
                }
                img.onerror = () => {
                    const err = new Error(`图片加载失败${url}`)
                    reject(err)
                }
                img.src = url
            })
        }
        // 正确的图片地址
		const url1 = 'https://profile.csdnimg.cn/4/8/2/3_qq_37987033'
		// 错误的图片地址
        const url2 = 'https://profile.csdnimgxx.cn/4/8/2/3_qq_37987033-error' 

		 async function readImg() {
            try {
                const img1 = await loadImg(url1)  
                console.log(img1.width, img1.height)
                const img2 = await loadImg(url2)
                console.log(img2.width, img2.height)
            } catch (error) {
                console.log(error)
            }
        }
        readImg()

输出如下👇
在这里插入图片描述

all 并行执行请求

// 模拟接口请求(真实开发中替换为fetch/axios)
const getUserInfo = () => new Promise(resolve => {
  setTimeout(() => resolve({ id: 1, name: '张三' }), 1000); // 1秒后返回用户信息
});
const getHomeList = () => new Promise(resolve => {
  setTimeout(() => resolve([{ id: 101, title: '首页文章1' }]), 800); // 800毫秒返回列表
});

try {
	const [userInfo, homeList] = await Promise.all([getUserInfo(), getHomeList()]);
	// 执行页面渲染逻辑
} catch {
	 console.error('Error fetching data:', error);
}

异步(微任务)

微任务指的是ES6之后和 Promise 相关的所有语法:

  • Promise、
  • async/await,
  • process.nextTick 在微任务中优先级最高

深入理解:

  • 在Promise 中 只有 then 中执行的是异步
  • 在async / await 在 await 下一行起执行的是异步

async function async1 () {
    console.log('async1 start') // 同步 2
    await async2() // 同步
    console.log('async1 end') // 7 await 后面的内容,异步(微任务),相当于 callback 函数里的内容
}

async function async2 () {
    console.log('async2') // 3
}

console.log('script start') //1


async1() // 同步

new Promise (function (resolve) {
    console.log('promise1') // 同步 4
    resolve()
}).then (function () {
    console.log('promise2') // 8
})

process.nextTick(() => { //6
    console.log('nextTick')
})

console.log('script end') // 同步完成 5

结果:

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐