这年头谁还不懂前端async&await?正是在下

前言

如题,在下只是一名不知名的后端小喽啰,之前也不懂为啥前端经常可以看到一堆 asyncawait,再或者是 promise。直到最近使用AI开发项目,JS代码是真的看得一愣一愣的😅。看来这年头,单干的独立开发,确实是要懂点前端啊。

有道是:君子生非异也,善假于物也。人要善于学习,不懂就学习嘛,而学习现在最便利的莫过于问AI了,AI多数时候真可以是一位知识渊博的老师,可以一路问问题问下去而不厌其烦地回答你。

进入正题

promiseasyncawait 的演进过程,其实可以简单的理解为前端 JavaScript 异步编程不断进化的过程,每个阶段都是对前一个阶段问题的改进。

promise有什么用

因为是先有 promise 后有 asyncawait。所以先从介绍 promise 开始哈。

根据马克思主义,一般新事物的出现是为了替代旧事物的存在,因而想知道 promise 有什么用,那么自然得看没有这货之前的前端是咋样的。

javascript 复制代码
function step1(a, callback) {
    console.log('Step 1:', a);
    callback(a + 1);
}

function step2(b, callback) {
    console.log('Step 2:', b);
    callback(b * 2);
}

function step3(c, callback) {
    console.log('Step 3:', c);
    callback(c - 3);
}

step1(5, (result1) => {
    step2(result1, (result2) => {
        step3(result2, (result3) => {
            console.log('Final Result:', result3);
        });
    });
});

参考AI的回答,写了上述0几年前端写回调的场景代码。

上述代码理解也很简单,就三个步骤,采用回调方式,每一步骤的结果作为后置步骤的输入,但是弊端是执行回调链路过于复杂。在实际执行时,如果执行步骤再多些,这个调用链路就该抓狂了,甚至于这里简化的调用如果加上判断每个步骤的异常报错情况,3步骤也很恐怖......

而在现代化的前端交互中,多步骤回调太稀疏平常了,就一个购物车结算流程:验证库存→计算价格→创建订单→处理支付→发送确认通知,都5步骤啦。

好了,再来看看新事物: promise 版本。

javascript 复制代码
function step1Promise(a) {
    return new Promise((resolve) => {
        console.log('Step 1:', a);
        resolve(a + 1);
    });
}

function step2Promise(b) {
    return new Promise((resolve) => {
        console.log('Step 2:', b);
        resolve(b * 2);
    });
}

function step3Promise(c) {
    return new Promise((resolve) => {
        console.log('Step 3:', c);
        resolve(c - 3);
    });
}

step1Promise(5)
    .then(result1 => step2Promise(result1))
    .then(result2 => step3Promise(result2))
    .then(result3 => {
        console.log('Final Result:', result3);
    });

看见没,实际执行时,已经不再是疯狂嵌套了,而是链式调用,这种明显优雅且维护性提高了不止一星半点。

也就是说:没有 promise 前,前端的嵌套回调场景,可能会是一个地狱 。而 promise 的出现,正是为了解决这一问题,让你活得更久。

那async又有什么用

回答这个问题前,来看看下述代码,猜猜返回啥。

javascript 复制代码
async function testAsync() {
    return 'hello world';
}
console.log(testAsync());

可能和你预期的不大一样,这里打印的不是 hello world,而是 Promise {<fulfilled>: 'hello world'} 。如果想获取实际结果,要这样:

javascript 复制代码
async function testAsync() {
    return 'hello world';
}
testAsync().then(result => console.log(result));

也就是说实际上,async 本质上是一个封装了的 Promise.resolve() ,相当于简化操作。

再看看继 promise 后的新兴事物: async 版的多层回调,是不是进一步简化了。

javascript 复制代码
async function step1Async(a) {
    console.log('Step 1:', a);
    return a + 1;
}

async function step2Async(b) {
    console.log('Step 2:', b);
    return b * 2;
}

async function step3Async(c) {
    console.log('Step 3:', c);
    return c - 3;
}

step1Async(5)
    .then(result1 => step2Async(result1))
    .then(result2 => step3Async(result2))
    .then(result3 => {
        console.log('Final Result:', result3);
    });

再说await有什么用

还是一样,先上代码。

javascript 复制代码
async function testAsync() {
    return 'hello world';
}
testAsync().then(result => console.log(result));
console.log(`${await testAsync()}`); 

你会发现,两种方式返回的是一样的效果,说白点,也就是 async 方法使用 await 可以直接获取到 promise.resolve() 中的值,也就是实际上 await 可以简单的理解为又是一个简化操作。

✍️冷知识:await 其实不仅可以用于 async ,也可以用于非 async 的方法,只是在非 async 方法中使用会直接返回方法 return 的数据。

可能你会问:那我用一个 promise 就能实现的功能,为啥要多此一举,专门弄这 async& await 两关键词呢?

说到底,跟有 promise 和没有 promise 有什么区别?其实是一个道理,都是为了更优雅更舒服地写代码,甚至更方便地调试代码。来看看使用 async&await 版本的多层回调。

javascript 复制代码
async function step1Async(a) {
    console.log('Step 1:', a);
    return a + 1;
}

async function step2Async(b) {
    console.log('Step 2:', b);
    return b * 2;
}

async function step3Async(c) {
    console.log('Step 3:', c);
    return c - 3;
}

const result1 = await step1Promise(5);
const result2 = await step2Promise(result1);
const result3 = await step3Promise(result2);
console.log('Final Result:', result3);

是不是可读性更佳,看着更像同步代码,调试代码啥的会更容易呢?

可能感知不是很明显,来看看某篇大佬文章的示例代码。

javascript 复制代码
function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(m, n) {
    console.log(`step2 with ${m} and ${n}`);
    return takeLongTime(m + n);
}

function step3(k, m, n) {
    console.log(`step3 with ${k}, ${m} and ${n}`);
    return takeLongTime(k + m + n);
}

promise 方式:

javascript 复制代码
function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => {
            return step2(time1, time2)
                .then(time3 => [time1, time2, time3]);
        })
        .then(times => {
            const [time1, time2, time3] = times;
            return step3(time1, time2, time3);
        })
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

async/await 方式:

javascript 复制代码
async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time1, time2);
    const result = await step3(time1, time2, time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();

是不是体验差距更明显了呢😁

补充说明

异步编程思想

虽然上述说明的示例代码都是同步操作的,但是大家要理解 asyncawait 从字面意思上来说更多的时候是用于 JavaScript 异步编程场景哈

💡题外话:实际上,JavaScript 语言本身是单线程模型,如果都采用同步调用,那么遇到阻塞性的操作时,如耗时的后端请求,那么前端页面的 JavaScript 交互渲染会在阻塞过程中直接卡死,完全用不了那种卡死。因而很多场景下你看到的前端代码都是需要采用异步回调的,这也就是为啥能经常看到代码中各种 asyncawait

IIFE(立即执行函数表达式)

某些场景下,无法直接在顶层执行 await 函数时,可以考虑使用 IIFE 方式执行呢。如下示例:

javascript 复制代码
(async () => {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
    const data = await response.json();
    console.log('API响应:', data);
  } catch (error) {
    console.error('错误:', error);
  }
})();

JS无法将异步操作真正变为同步

前些天,刚开始咨询 AI 的时候,我也理解岔了,还自信发现新大陆似的去知乎发想法了,真丢人。

要注意即便将代码调整为 await ,也不是真正的同步操作。按照 JavaScript 的事件处理机制:

javascript 复制代码
// 事件循环的工作原理
while (true) {
  // 1. 执行所有同步任务
  // 2. 检查微任务队列(Promise.then, async/await)
  // 3. 检查宏任务队列(setTimeout, setInterval, I/O)
  // 4. 渲染(如果是在浏览器中)
  // 5. 重复
}

在执行 await 的时候,会立即返回 promise 到主线程,继续同步操作。而异步操作的 async 方法,会在所有同步任务执行完毕后,再陆续执行。下述是示例代码,仅作为补充说明:

javascript 复制代码
async function example() {
  console.log('1. async函数开始');
  
  // await 实际上:
  // 1. 暂停当前函数的执行
  // 2. 将函数剩余部分包装为微任务
  // 3. 立即返回一个 Promise
  const promise = fetch('/api/data');
  console.log('2. 创建了Promise,但未等待');
  
  const data = await promise;  // 暂停点
  console.log('4. 恢复执行,收到数据');
  
  return data;
}

console.log('A. 调用async函数前');
const resultPromise = example();  // 立即返回Promise,不阻塞
console.log('B. async函数已调用,获得Promise对象');

resultPromise.then(data => {
  console.log('C. async函数最终完成');
});

console.log('D. 继续执行其他同步代码');

// 输出顺序:
// A. 调用async函数前
// 1. async函数开始
// 2. 创建了Promise,但未等待
// B. async函数已调用,获得Promise对象
// D. 继续执行其他同步代码
// 4. 恢复执行,收到数据
// C. async函数最终完成

好啦,以上就是今天分享的内容,感谢阅读,欢迎三连!

相关推荐
百度地图汽车版5 小时前
【AI地图 Tech说】第二期:一文解码百度地图ETA
前端
恋猫de小郭5 小时前
罗技鼠标因为服务器证书过期无法使用?我是如何解决 SSL 证书问题
android·前端·flutter
Sailing6 小时前
AI 流式对话该怎么做?SSE、fetch、axios 一次讲清楚
前端·javascript·面试
橙露6 小时前
Vue3 组件通信全解析:技术细节、适用场景与性能优化
前端·javascript·vue.js
扉间7986 小时前
lightrag嵌入思路
前端·chrome
toooooop86 小时前
Vuex Store实例中`state`、`mutations`、`actions`、`getters`、`modules`这几个核心配置项的区别
前端·javascript·vue.js
LYFlied6 小时前
Rust代码打包为WebAssembly二进制文件详解
开发语言·前端·性能优化·rust·wasm·跨端
OpenTiny社区6 小时前
历时1年,TinyEditor v4.0 正式发布!
前端·javascript·vue.js
time_rg6 小时前
深入理解react——1. jsx与虚拟dom
前端·react.js