{calculateTotal(items," />
  1. 主页 > 好文章

JS异步编程全解析:从Promise到Async Await实战应用

你写的网页是不是经常像老年手机一样卡成PPT?点个按钮转圈圈半天,最后弹个"网络异常"?别慌!今天就带你破解JS异步编程的"祖传难题",看完这篇包你代码丝滑得像德芙巧克力!


一、先搞懂这锅"粥"是怎么煮糊的

举个活生生的例子——电商购物车加载:

javascript复制
// 老式回调写法(新手千万别学!)
getCartItems(userId, (items) => {
    calculateTotal(items, (total) => {
        applyCoupons(total, (finalPrice) => {
            updateUI(finalPrice)  // 到这里已经头晕了
        })
    })
})

这代码像不像煮糊的粥???三大致命伤??:

  1. 错误处理要每个回调都写一遍(累不累啊)
  2. 改需求等于重写(产品经理狂喜)
  3. 内存泄漏防不胜防(页面越用越卡)

二、Promise:给你的代码装个进度条

想象在餐厅排队取号的感觉,Promise就是这个电子叫号器:

javascript复制
// 改造后的购物车流程
fetchCartItems(userId)
    .then(items => calculateTotal(items))
    .then(total => applyCoupons(total))
    .then(finalPrice => {
        updateUI(finalPrice)
        return sendToPayment(finalPrice)  // 还能接着链式调用
    })
    .catch(error => {
        console.log('哪个环节翻车了:', error)
    })

??必记的五个知识点??:

  • .then()里的返回值会自动包装成新Promise
  • Promise.all()适合并发多个请求(比如同时加载商品图和详情)
  • Promise.race()用来做超时控制(移动端必备)
  • 链式调用不是必须垂直对齐(别被代码格式绑架)
  • 永远在最后加.catch()(保命符啊!)

常见翻车现场:

javascript复制
// 错误示范!
loadData().then(result => {
    setTimeout(() => {  // 这里埋了个定时炸弹
        return process(result)
    }, 1000)
})
// 正确姿势要加return
loadData().then(result => {
    return new Promise(resolve => {
        setTimeout(() => resolve(process(result)), 1000)
    })
})

三、Async/Await:把异步写成同步的"障眼法"

这玩意儿就像网购时查物流,虽然要等快递,但不用盯着手机看:

javascript复制
async function checkoutFlow() {
    try {
        const items = await fetchCartItems()     // 等购物车数据
        const total = await calculateTotal(items)  
        const finalPrice = await applyCoupons(total)
        
        // 并发优化技巧(同时请求支付和更新库存)
        const [paymentResult, stockUpdate] = await Promise.all([
            initPayment(finalPrice),
            updateInventory(items)
        ])
        
        return showSuccessPage(paymentResult)
    } catch (error) {
        // 统一错误处理
        if(error instanceof PaymentError) {
            showError('银行卡余额不足')
        } else {
            showError('系统开小差了')
        }
    }
}

??三大实战技巧??:

  1. Promise.all()打包多个await(提速神器)
  2. 在循环里慎用await(改用for...of逐条处理)
  3. 记得给异步函数起名字(调试时能看清调用栈)

特殊场景处理:

javascript复制
// 文件上传进度条
async function uploadFile(file) {
    const reader = new FileReader()
    
    // 传统事件监听转Promise
    const readPromise = new Promise(resolve => {
        reader.onload = resolve
    })
    
    reader.readAsDataURL(file)
    await readPromise
    
    // 显示预览图
    previewImage(reader.result)
}

四、Promise和Async/Await的"爱恨情仇"

通过对比看门道:

场景Promise方案Async/Await方案
错误处理.catch()统一处理try/catch块包裹
条件判断需要嵌套then可以直接用if语句
中间值传递需要层层传递变量直接赋值
调试难度断点难跟踪跟同步代码一样
浏览器兼容性IE11+需要Babel转译

五、自问自答时间

Q:学了Async还要用Promise吗?
A:必须的!就像会开车也得懂点修车,遇到Promise.all()这种场景还是原生的香

Q:await会阻塞页面渲染吗?
A:完全不会!JS引擎的事件循环机制在背后默默工作,你尽管await,渲染交给浏览器

Q:怎么取消异步操作?
A:上AbortController这个神器:

javascript复制
const controller = new AbortController()

// 发请求时带上signal
fetch('/api/data', {
    signal: controller.signal
})

// 需要取消时调用
controller.abort()

六、移动端专属优化技巧

  1. ??弱网处理??:给每个请求加超时
javascript复制
function fetchWithTimeout(url, timeout=5000) {
    return Promise.race([
        fetch(url),
        new Promise((_, reject) =>
            setTimeout(() => reject(new Error('请求超时')), timeout)
        )
    ])
}
  1. ??省电模式??:用requestIdleCallback处理非紧急任务
  2. ??内存优化??:及时清理未完成的Promise

小编说句掏心窝的

干了八年前端的老司机送你三句话:

  1. ??别瞧不起回调函数??(某些库必须用回调)
  2. ??Async/Await不是银弹??(复杂流程结合Promise更香)
  3. ??先写能跑的代码??(优化是后面的事)

最后提醒新手:看见await后面跟个同步函数?赶紧删掉!这就像用跑车送外卖——不是不行,但真没必要!

本文由嘻道妙招独家原创,未经允许,严禁转载