资讯专栏INFORMATION COLUMN

async/await 和 Promise 的用例&关系

y1chuan / 796人阅读

摘要:和的关系和的关系非常的巧妙,必须在内使用,并装饰一个对象,返回的也是一个对象。的会使得的代码节点自动抛出相应的异常,终止向下继续执行。示例方法内无节点没有修饰的方法内有节点注意内的对节点的作用。

假设我们有三个请求,req1,req2, req3,三个请求后者依赖前者的请求结果。我们先使用Promise封装一个异步请求的方法。

Promise 异步请求

使用Promise可以非常容易的封装一个异步处理的业务,通过reslove/reject两个callback来返回执行结果。

我们使用 Promise 封装一个 http get方法。

// 返回一个 Promise 对象(PromiseStatus: pending)
function asyncHttpGet(url) {
    return new Promise((resolve, reject) => {
        const request = new Request(url, {method: "GET"})
        // 使用 fetch 请求
        fetch(request)
            .then(response => {
                if (200 == response.status) {
                    return response.text()
                } else {
                    // goto catch
                    throw new Error("request failed: " + response.status)
                }
            }).then(html => {
                // console.log(html)
                resolve(url + " get success")
            }).catch(err => {
                reject(err)
            });
    })
}

fetch返回的其实就是一个Promise对象,上例是想演示下resolve/reject的使用上下文,如果你早已get,下面给出直接使用fetch的方式:

async function asyncHttpGetV2(url) {
    const request = new Request(url, {method: "GET"})
    try {
        let res = await fetch(request)
            .then(response => response.blob())
            .then(blob => {
                console.log(blob)
                // Promise resolve
                return blob
            }).catch(err => {
                // Promise resolve
                throw err
            });
        // Promise resolve
        return res;
    } catch (err) {
        // Promise reject
        throw err
    }
}

可以发现,fetchreturn 代替了resolvethrow代替了reject,而asyncfetch一样,也是返回了一个 Promise对象,所以async中的return/throw是否也会与自己的返回的Promise对象有关系呢?

回调地狱

Promise 可以优雅的实现异步,但 Promise.then().catch() 的链式结构也带来了回调地狱的问题。如下,我们回调了3层,才能开始写业务逻辑。

var url = window.location.href
// 虽然异步了 但 callback hell
asyncHttpGet(url).then(res => {
    var res1 = res
    asyncHttpGet(url).then(res => {
        var res2 = res
        asyncHttpGet(url).then(res => {
            var res3 = res
            console.log(res1, res2, res3);
            // todo 业务
        }).catch(err => {
            console.log(err)
        })        
    }).catch(err => {
        console.log(err)
    })
}).catch(err => {
    console.log(err)
})
async/await

借助 aysnc/await 解决回调地狱的问题,实现同步风格的异步逻辑,这里希望大家能理解透2 & 3两条总结:

aysnc 返回的也是一个 Promise 对象。

如果返回了return 标量throw Error 则返回 {PromiseStatus: resolved/rejected}Promise对象。

如果遇到了await装饰的Promise,则返回 {PromiseStatus: pending}Promise。并等待此Promise的执行结果:如果Promise触发了resolve则获取结果并继续向下执行;如果Promise触发了reject则抛出一个异常。所以我们在使用时应将代码使用try...catch封装。

await 关键字只能在 async内使用,await主要意图是装饰一个以 {PromiseStatus: pending}的状态返回的Promise对象(任何 JS 表达式都可以,但没什么意义),并等待其后续的resolved/rejected状态更新,从而决定是获得结果并继续向下执行,还是终止并抛出异常。

var url = window.location.href

async function getUrls(url1, url2, url3) {
    try {
        // req1 success or throw error (promise reject)
        let res1 = await asyncHttpGet(url1);
        
        // req2 success or throw error (promise reject)
        let res2 = await asyncHttpGet(url2);
        
        // req3 success or throw error (promise reject)
        let res3 = await asyncHttpGet(url3);
        
        // 三个异步请求都成功 获取最终结果
        return [res1, res2, res3].join("
")
    } catch(err) {
        // 出现错误,做一些处理
        console.log(err)
        throw err
    }
}

// 如此 3 个 Promise 请求在 async/await 的封装下变成了一个同步书写风格的异步请求
getUrls(url, url, url).then(res => {
    console.log(res)
    // todo 业务
}).catch(err => {
    console.log(err)
})

console.log("request has been sended, and waiting for res")

async 返回的是 Promise对象,所以我们还可以继续使用 asyncawait封装异步到同步风格。

async function getUrlsMore(url1, url2) {
    try {
        let getUrls1 = await getUrls(url1, url1, url1)
        let getUrls2 = await getUrls(url2, url2, url2)
        
        // Promise resolve
        return [getUrls1, getUrls2].join("
")
    } catch (err) {
        // Promise reject
        throw err
    }
}

getUrlsMore(url, url).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})
async/await 和 Promise 的关系

async/awaitPromise 的关系非常的巧妙,await必须在async内使用,并装饰一个Promise对象,async返回的也是一个Promise对象。

async/await中的return/throw会代理自己返回的Promiseresolve/reject,而一个Promiseresolve/reject会使得await得到返回值或抛出异常。

如果方法内无await节点

return 一个字面量则会得到一个{PromiseStatus: resolved}Promise
throw 一个Error则会得到一个{PromiseStatus: rejected}Promise

如果方法内有await节点
async会返回一个{PromiseStatus: pending}Promise(发生切换,异步等待Promise的执行结果)。
Promiseresolve会使得await的代码节点获得相应的返回结果,并继续向下执行。
Promisereject 会使得await的代码节点自动抛出相应的异常,终止向下继续执行。

示例:

方法内无await节点
// 没有 await 修饰的 Promise
async function foo() {
    if (Math.ceil(Math.random() * 10) > 5) {
        // {PromiseStatus: resolved}
        return "hello world"
    } else {
        // {PromiseStatus: rejected}
        throw new Error("something wrong!")
    }
}

var fooPromise = foo()
console.log(fooPromise)

fooPromise.then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

resolved

rejected

方法内有await节点

注意Promise内的resolve/rejectawait节点的作用。

async function bar() {
    try {
        // await 返回 {PromiseStatus: pending}
        let res = await new Promise((resolve, reject) => {
            setTimeout(() => {
                if (Math.ceil(Math.random() * 10) > 5) {
                    // await 获得结果并继续执行
                    resolve("success")
                } else {
                    // await 中断执行并抛出异常
                    reject("failed")
                }
            }, 2000)
        })
        // resolve {PromiseStatus: resolved}
        return res
    } catch (err) {
        // reject {PromiseStatus: rejected}
        throw err
    }
}

var barPromise = bar()

// 查看 barPromise 的 PromiseStatus
console.log(barPromise)

barPromise.then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})
await配合fetch的实例

then/catch返回的也是Promise对象,在then/catch内使用return/throw来决定返回的Promiseresolved/rejected

// 没有 await 修饰的 Promise
async function bar() {
    try {
        // await 返回 {PromiseStatus: pending}
        let res1 = await fetch(window.location.href).then(res => {
            if (200 == res.status) {
                // Promise resolve
                return "request success"
            } else {
                // goto catch
                throw "request failed" + res.status
            }
        }).catch(err => {
            // Promise reject
            throw err
        })
        
        let res2 = await fetch(window.location.href).then(res => {
            if (200 == res.status) {
                // Promise resolve
                return "request success"
            } else {
                // goto catch
                throw "request failed" + res.status
            }
        }).catch(err => {
            // Promise reject
            throw err
        })
        
        let res3 = await fetch(window.location.href).then(res => {
            if (200 == res.status) {
                // Promise resolve
                return "request success"
            } else {
                // goto catch
                throw "request failed" + res.status
            }
        }).catch(err => {
            // Promise reject
            throw err
        })
        
        // 三个请求都成功 则返回相应的数据 Promise resolved
        return [res1, res2, res3].join("
")
    } catch (err) {
        // Promise rejected
        throw err
    }
}

var barPromise = bar()

// 查看 barPromise 的 PromiseStatus
console.log(barPromise)

// Promise reject 抛出异常 需要使用 catch 捕捉
barPromise.then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/110298.html

相关文章

  • Promise && async/await的理解用法

    摘要:但是中的这种情况与抽象反应器模式如何描述完全不同。在处理一个阶段之后并且在移到下一个队列之前,事件循环将处理两个中间队列,直到中间队列中没有剩余的项目。如果没有任务则循环退出,每一次队列处理都被视为事件循环的一个阶段。 Promise && async/await的理解和用法 为什么需要promise(承诺)这个东西 在之前我们处理异步函数都是用回调这个方法,回调嵌套的时候会发现 阅读...

    王笑朝 评论0 收藏0
  • async & await & promise

    摘要:最近项目中用的比较多,所以特地去了解,模仿一下实现先来看看使用的方法通过是通过使用生成器配合方法模拟的一个同步操作,这个技术有效的避免了传统回调和形成的回调地狱。 最近项目中 asyn & await 用的比较多,所以特地去了解,模仿一下实现~ 先来看看 使用 async & await 的方法 async function d () { try { const a = a...

    Dean 评论0 收藏0
  • async & await (译)

    摘要:的出现,让我们可以走出回调地狱,着实惊艳。我已经开始使用里的和关键字来简化的处理。异步任务在这个例子是执行之后,一直在执行完成才继续下一个任务并没有产生阻塞。最后这个函数处理了返回值并且返回了一个对象。依然很棒,但和使得它可维护性更好。 JavaScript Promises的出现,让我们可以走出回调地狱,着实惊艳。Promises 允许我们更好的引入和处理异步任务,虽然如此,但引入好...

    The question 评论0 收藏0
  • setTimeout&Promise&Async之间的爱恨情仇

    摘要:但是提出标准,允许脚本创建多个线程,但是子线程完全受主线程控制。只是将事件插入了任务队列,必须等到当前代码执行栈执行完,主线程才会去执行它指定的回调函数。之后全局上下文进入函数调用栈。 setTimeout 一、setTimeout 初现 定义:setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。 语法: setTimeout(code, millisec...

    Half 评论0 收藏0
  • js异步从入门到放弃(实践篇) — 常见写法&面试题解析

    摘要:前文该系列下的前几篇文章分别对不同的几种异步方案原理进行解析,本文将介绍一些实际场景和一些常见的面试题。流程调度里比较常见的一种错误是看似串行的写法,可以感受一下这个例子判断以下几种写法的输出结果辨别输出顺序这类题目一般出现在面试题里。 前文 该系列下的前几篇文章分别对不同的几种异步方案原理进行解析,本文将介绍一些实际场景和一些常见的面试题。(积累不太够,后面想到再补) 正文 流程调度...

    Awbeci 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<