async,await
·
await等待的不是异步函数本身,而是delay函数执行后返回的Promise对象。
一.先明确两个关键概念
async函数的特性:任何async函数执行后,必然返回一个 Promise 对象(哪怕你写async function fn() { return 1 },也会被包装成Promise.resolve(1));await的作用:只能跟 Promise 对象(非 Promise 会被自动包装成Promise.resolve(xxx)),并等待这个 Promise 从pending状态变为fulfilled/rejected后,才继续执行后续代码。
二.常见误区澄清
-
误区:
await等的是delay函数执行的 “耗时”❌ 错:delay函数执行本身是 “瞬间完成” 的(只是创建了一个 Promise 并返回),耗时的是 Promise 内部的setTimeout;✅ 对:await等的是 Promise 从pending到fulfilled的状态变化,而这个变化由setTimeout触发。 -
误区:
async函数里必须有await❌ 错:async只是标记函数返回 Promise,哪怕没有await,比如:async function fn() { return 1; } console.log(fn()); // 输出 Promise {<fulfilled>: 1}
三.await 核心功能之一就是「解包 Promise」 —— 它会等待 Promise 状态变为 fulfilled,并直接返回 Promise 内部 resolve 的值(相当于把 Promise 包裹的 “结果” 拆出来);如果 Promise 被 rejected,则会抛出错误(需用 try/catch 捕获)。
Promise 并不是 “必须用箭头函数作为参数”,而是 Promise 构造函数要求接收一个「执行器函数(executor)」作为参数 —— 这个执行器函数可以是箭头函数、普通函数声明、函数表达式等任意合法的函数形式,箭头函数只是最常用的写法(简洁、无 this 绑定问题)。
下面从「核心定义、参数本质、不同写法对比、执行器函数的作用」四个维度,把这个问题讲透:
一、Promise 构造函数的核心定义
先看 Promise 官方的构造函数签名:
new Promise(executor);
executor:必须是一个函数(这是唯一要求),这个函数会被 Promise 立即执行,且接收两个内置参数:resolve(成功回调)和reject(失败回调)。- 箭头函数只是
executor的 “写法之一”,不是 “语法要求”。
二、Promise 参数的本质:执行器函数(executor)
Promise 构造函数的参数是一个「函数」,这个函数的核心特征:
- 必须接收
resolve和reject两个参数(顺序固定,名称可自定义,但行业惯例用这两个名字); - 被 Promise 立即同步执行(Promise 一创建,这个函数就会执行);
- 内部通常包裹异步操作(比如 setTimeout、接口请求),最终调用
resolve/reject改变 Promise 状态。
三、Promise 参数的多种写法(箭头函数 vs 普通函数)
写法 1:箭头函数(最常用,推荐)
这是你代码中用的写法,优点是简洁、无 this 绑定(执行器函数中的 this 无实际意义,箭头函数更干净):
// 你的代码:箭头函数作为 executor
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功"); // 异步操作完成后调用 resolve
}, 3000);
});
写法 2:普通函数表达式(合法,但冗余)
// 普通函数表达式作为 executor
const p = new Promise(function(resolve, reject) {
setTimeout(() => {
resolve("成功");
}, 3000);
});
写法 3:独立函数声明(拆分逻辑,适合复杂场景)
如果执行器内部逻辑复杂,可以把函数抽成独立声明:
// 独立声明执行器函数
function executor(resolve, reject) {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve("成功");
} else {
reject("失败");
}
}, 3000);
}
// 传入 Promise 构造函数
const p = new Promise(executor); // 注意:只传函数名,不要加括号(加括号会立即执行)
写法 4:甚至可以用匿名函数(不推荐,可读性差)
const p = new Promise(function(res, rej) { // 参数名自定义(不推荐,不符合惯例)
setTimeout(() => res("成功"), 3000);
});
四、关键细节:执行器函数的核心作用
不管用哪种函数写法,执行器函数的核心使命是:
- 启动异步操作(比如 setTimeout、fetch、文件读取);
- 在异步操作完成后,调用
resolve/reject改变 Promise 状态:- 调用
resolve(value):Promise 状态从pending→fulfilled(成功),并把value作为结果保存; - 调用
reject(reason):Promise 状态从pending→rejected(失败),并把reason作为错误保存;
- 调用
- 同步执行特性:Promise 一创建,执行器函数就会立即执行(哪怕内部是异步操作):
console.log("1. 开始创建 Promise"); const p = new Promise((resolve) => { console.log("2. 执行器函数执行"); // 这行在创建 Promise 时立即打印 setTimeout(() => { console.log("3. 异步操作完成"); // 3秒后打印 resolve(); }, 3000); }); console.log("4. Promise 创建完成"); // 打印顺序:1 → 2 → 4 → 3
五、为什么箭头函数是主流写法?
虽然 Promise 不强制用箭头函数,但箭头函数有两个核心优势,成为行业惯例:
- 语法简洁:少写
function关键字,代码更紧凑(对比普通函数表达式,少了function和大括号的冗余); - 无 this 绑定问题:执行器函数中的
this本身没有实际作用(Promise 内部会忽略 this),箭头函数的this继承自外层作用域,不会出现普通函数中this指向全局 /window 的问题(避免不必要的 bug); - 符合异步代码的书写习惯:现代 JS 中异步操作(setTimeout、fetch 回调)普遍用箭头函数,风格统一。
六、常见误区澄清
- 误区:Promise 参数必须是箭头函数❌ 错:只要是接收
resolve/reject的函数即可,箭头函数只是写法之一; - 误区:执行器函数可以不传
resolve/reject❌ 错:可以不传,但 Promise 会永远处于pending状态(无法完成),失去意义; - 误区:执行器函数是异步的❌ 错:执行器函数本身是同步执行的,只是内部通常包裹异步操作。
async 函数并不会 “主动调用 resolve 方法”,但它的底层逻辑会自动将函数的返回值 / 异常映射为 Promise 的 resolve/reject 行为
更多推荐
所有评论(0)