共计 5104 个字符,预计需要花费 13 分钟才能阅读完成。
假设我们有三个请求,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 | |
} | |
} |
可以发现,fetch
中 return
代替了resolve
,throw
代替了 reject
,而async
同fetch
一样,也是返回了一个 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
接受一个Promise
对象,并将其以{PromiseStatus: pending}
的状态返回。
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("\n") | |
} 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
对象,所以我们还可以继续使用 async\await
封装异步到同步风格。
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("\n") | |
} catch (err) { | |
// Promise reject | |
throw err | |
} | |
} | |
getUrlsMore(url, url).then(res => {console.log(res) | |
}).catch(err => {console.log(err) | |
}) |
async/await 和 Promise 的关系
async/await
和 Promise
的关系非常的巧妙,await
必须在 async
内使用,并装饰一个 Promise
对象,async
返回的也是一个 Promise
对象。
async/await
中的 return/throw
会代理自己返回的 Promise
的resolve/reject
,而一个 Promise
的resolve/reject
会使得 await
得到返回值或抛出异常。
如果方法内无 await
节点
return
一个 字面量
则会得到一个 {PromiseStatus: resolved}
的Promise
。throw
一个 Error
则会得到一个 {PromiseStatus: rejected}
的Promise
。
如果方法内有 await
节点 async
会返回一个 {PromiseStatus: pending}
的Promise
(发生切换,异步等待 Promise
的执行结果)。Promise
的 resolve
会使得 await
的代码节点获得相应的返回结果,并继续向下执行。Promise
的 reject
会使得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/reject
对 await
节点的作用。
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
来决定返回的 Promise
是resolved/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("\n") | |
} 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) | |
}) |