假設咱們有三個請求,req1,req2, req3,三個請求後者依賴前者的請求結果。咱們先使用Promise
封裝一個異步請求的方法。html
使用Promise
能夠很是容易的封裝一個異步處理的業務,經過reslove/reject
兩個callback
來返回執行結果。promise
咱們使用 Promise
封裝一個 http get
方法。dom
// 返回一個 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
對象有關係呢?async
Promise
能夠優雅的實現異步,但 Promise.then().catch()
的鏈式結構也帶來了回調地獄的問題。以下,咱們回調了3層,才能開始寫業務邏輯。fetch
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) })
藉助 aysnc/await
解決回調地獄的問題,實現同步風格的異步邏輯,這裏但願你們能理解透2 & 3
兩條總結:url
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("\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
封裝異步到同步風格。spa
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
的關係很是的巧妙,await
必須在async
內使用,並裝飾一個Promise
對象,async
返回的也是一個Promise
對象。代理
async/await
中的return/throw
會代理本身返回的Promise
的resolve/reject
,而一個Promise
的resolve/reject
會使得await
獲得返回值或拋出異常。code
若是方法內無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) })