async/await 和 promise/promise.all 的示例

概述

  1. promise 为 js 提供了 并发异步 能力,有 回调地狱 的问题。
  2. async/await 可以使一批 promise同步依次 的模式去执行(一些接口依赖场景是有此需求的),解决 回调地狱 的问题。
  3. promise.all 可以等待一批 promise 任务 全部执行完成 后返回,一并返回 结果集合,适合子任务无相互依赖,但 后续任务 需依赖 前多个子任务的数据结果 的场景。

Promise / Promise.all

Promise 提供了异步接口,搭配 resolve/reject 可以很方便的让业务代码并发执行。

function promiseReq(url) {
    return new Promise((resolve, reject) => {
        fetch(url).then((res) => {
            resolve(res)
        }).catch((e) => {
            reject(e)
        })
    })
}
//
promiseReq(url1)
.then(res => console.log(res))
.catch(e => console.error(e))
promiseReq(url2)
.then(res => console.log(res))
.catch(e => console.error(e))

console.log("promise1 promise2 execute")

Promise.all 则是并发执行任务集合,且等待所有的任务执行完成后,一并返回结果集。

async / await

async 正如其名,用于定义一个异步方法,它会自动将方法封装成一个 Promise 返回,并且使用 return 代表 resolvethrow 代表 reject

async function asyncFoo() {
    if (Math.floor(Math.random() * 10) > 5) {
        return "asyncFoo resolved"
    }
    throw "asyncFoo rejected"
}

console.log(asyncFoo() instanceof Promise)

asyncFoo().then((res) => {
    console.log(res)
}).catch((e) => {
    console.error(e)
})

但日常开发中我们并不会单独使用 async,而是搭配 await 去同步多个 promise 任务。await 的作用就是让 Promise 的异步回调降维至同步模式。

场景1 - 接口并发执行且数据结果汇总

当需要等待一批接口的数据全部返回后,才可以继续执行后面的接口时,则可以使用 Promise.all 来处理 一批任务。其会 并发 执行 任务集合 中的 Promise 任务,等待所有的任务执行完成后,汇总结果并返回(结果数组)。

比如刷新用户信息接口,1.请求基础数据 2.请求订单数据 3.请求邀请数据 4.刷新完成,4 要等待 1、2、3 全部执行完成后才可执行,那就很适用 Promise.all([1,2,3]).then((res) => {4}) 的场景。

function promiseEnum(countDown) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("promiseEnum countDown " + countDown)
        }, countDown * 1000)
    })
}

Promise.all([promiseEnum(1), promiseEnum(2), promiseEnum(3)])
.then((values) => {
    // 等待 1 2 3 并发执行完成
    console.log("Promise.all values", values)
    promiseEnum(4).then((res4) => {
        console.log(res4)
    })
})

场景2 - 接口顺序依赖执行

试想一下,有4个数据接口,后者依赖前者返回的结果才能继续执行,如果使用传统的 Promise,那大概就是

// callback hell
promiseReq(url1).then((res1) => {
    promiseReq(url2 + res1).then((res2) => {
        promiseReq(url3 + res2).then((res3) => {
            promiseReq(url4 + res3).then((res4) => {
                console.log("promiseReq finished")
            }).catch(err => console.error(err))
        }).catch(err => console.error(err))
    }).catch(err => console.error(err))
}).catch(err => console.error(err))

使用 async/await 则可以友好的解决依赖回调问题,让代码以 同步 的风格编写和执行。

// 使用 async/await 则可以友好的解决依赖回调问题
async function promiseSyncReq() {
    let res1 = await promiseReq(url1)
    let res2 = await promiseReq(url2 + res1)
    let res3 = await promiseReq(url3 + res2)
    let res4 = await promiseReq(url4 + res3)
    return res4
}
promiseSyncReq().then((res4) => {
    console.log(res4)
}).catch(e => console.log(err))

async/await 的执行耗时等于各个 Prmoise 累计的总耗时(执行流本就是要串行,耗时自然是各接口的累计,回调模式也一样的)。

async function awaitAllPromise() {
    let res1 = await promiseEnum(1)//阻塞等待 耗时1秒
    let res2 = await promiseEnum(2)//阻塞等待 耗时2秒
    let res3 = await promiseEnum(3)//阻塞等待 耗时3秒
    let res4 = await promiseEnum(4)//阻塞等待 耗时4秒
    
    //执行总耗时为各 Promise 耗时的累加
    return [res1, res2, res3, res4]
}

组合使用

async/awaitpromise/promise.all 组合使用,灵活实现 部分任务并发执行部分任务同步执行

async function batchExec() {
    console.group("batchExec")

    let startTime = new Date().getTime()
    console.log("batchExec start", startTime)
    
    // 等待 1,2,3 并发执行完成
    let paralValues = await Promise.all([promiseEnum(1), promiseEnum(2), promiseEnum(3)])

    let paralTime = new Date().getTime()
    console.log("parallel 1,2,3 finished", paralTime, (paralTime - startTime) / 1000, paralValues)

    // 再继续执行 4
    let res4 = await promiseEnum(4)

    let endTime = new Date().getTime()
    console.log("batchExec end", endTime, (endTime - startTime) / 1000)

    console.groupEnd("batchExec")

    return [...paralValues, res4]
}

batchExec()
.then(res => console.log(res))
.catch(e => console.error(e))

规范至上

1.7k 声望
121 粉丝
0 条评论
推荐阅读
linux 用户/组相关操作
查看所有用户/组 {代码...} 添加用户useradd 选项 用户名 {代码...} {代码...} 删除用户 {代码...} 设置/更新用户密码 {代码...} 更改用户属性usermod 选项 用户名 {代码...} 为用户增加sudo权限root 查看 /etc/s...

big_cat阅读 486

从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木150阅读 12.3k评论 10

正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...

寒青56阅读 7.9k评论 11

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy46阅读 6k评论 12

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木66阅读 6.2k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.3k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木44阅读 7.4k评论 6

规范至上

1.7k 声望
121 粉丝
宣传栏