Promise 是非同步程式設計的一種解決方案。
Promise
/**
* 屬性
*/
Promise.length
Promise.prototype
/**
* 方法
*/
Promise.all(iterable) // 所有成功觸發成功 任何失敗觸發失敗
Promise.race(iterable) // 任意一個成功或失敗後觸發
Promise.reject(reason)
Promise.resolve(value)
/**
* 原型
*/
Promise.prototype.constructor
//方法
Promise.prototype.catch(onRejected)
Promise.prototype.then(onFulfilled, onRejected)
Promise.prototype.finally(onFinally)
Promise 有三種狀態
- pending: 初始狀態,既不是成功,也不是失敗狀態。
- resolve: 意味著操作成功完成。(resoloved)
- reject: 意味著操作失敗。
pending
pending 是初始狀態,執行 resolve/reject 會進入對應狀態,如果不執行,責一直為 pending 狀態
例如下面程式碼,promise 將一直在 pending 狀態,不會執行 then/catch.
new Promise(function (resolve, reject) { })
.then(res => console.log(res))
.catch(err => console.log(err))
resolve
resolve 意味著操作成功完成, 如果有 .then,值會傳入 .then 的第一個引數函式裡。
如
new Promise(function (resolve, reject) {
resolve(1)
})
.then(res => console.log(res))
then 的第一個引數是成功的回撥,第一個引數的返回值會影響接下來鏈的去向。第一個引數的返回值一般有三種情況
- 無返回值:會去執行下一個 .then ,沒有引數
- 返回值非promise:呼叫下一個then的函式,引數為返回值
- 返回值為promise:根據promise的執行結果,執行 下一個then/catch,如果一直是pending,則不執行下一個then/catch
例如想要在當前 then 終止,可以這樣操作:
.then((res) => new Promise(() => {}))
reject
reject 意味著操作失敗。
使用 .catch 會捕獲到錯誤資訊。
與程式碼報錯(如 undefined.a)不同的是, 程式碼報錯如果不使用 catch 捕獲,會向外傳遞,最終傳遞到根結點;而 reject 屬於 promise 錯誤,即使不使用 catch 捕獲也不會對全域性有影響。
用 promise 實現 fetch
先來看幾個問題:
- 如果請求 code 404, 會走 then 還是 catch? (答案:then)
- 控制檯能看到一行 404 的錯誤, 為什麼還是走 then 不是 catch 呢
- 如果請求跨域失敗,走 then 還是 catch?(答案:catch)
- 同樣是控制檯看到錯誤,兩者有什麼區別呢?
- 跨域失敗的報錯, 和 then 中 undefined.a 報錯,如果都不 catch,後者在 react 腳手架開發環境頁面會蹦,兩者有什麼區別?
帶著這幾個問題,來看看 fetch。
fetch 返回值是 promise,所以有三種狀態 pending、resolve、reject.
- pending: 請求中
- resolve: 請求成功(code 200/404/500 等, 非 200 控制檯輸出錯誤)
- reject: 請求失敗(跨域失敗、連線超時、無網路等,控制檯輸出錯誤)
我們還發現,請求失敗時,只能 catch 到最後一行錯誤, 如圖
捕獲後
為什麼 404 在控制檯看到錯誤,還走 then, resolve 如何實現
實現有幾個難點,
- throw 後面程式碼不會執行;
- 先報錯,後執行 then;
- catch 後錯誤不會列印在控制檯;
試了下,Promise.reject('xxx') 這樣的報錯方式雖然是微觀任務,但是總是在.then之後才在控制檯輸出,更像是巨集觀任務。所以也加個setTImeout巨集觀任務調至後面。
var fetch = function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
if ('請求成功 200') {
resolve('Response資料結構');
} else if ('請求成功 404,500等') {
Promise.reject('GET xxxxxxxx 404');
setTimeout(function () {
resolve('Response資料結構');
});
}
})
})
}
請求失敗 例如跨域失敗 reject 如何實現呢
同樣加個 setTimeout
var fetch = function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
if ('請求成功 200') {
resolve('Response資料結構');
} else if ('請求成功 404,500等') {
Promise.reject('GET xxxxxxxx 404');
setTimeout(function () {
resolve('Response資料結構');
});
} else if ('請求失敗') {
Promise.reject('Access to fetch xxxxx with CORS disabled.');
Promise.reject('GET xxxxx net::ERR_FAILED');
setTimeout(function () {
reject('TypeError: Failed to fetch');
});
}
})
})
}
還是有些問題,我們實現的因為在promise 中,錯誤會有字首 Uncaught (in promise)。瀏覽器客戶端應該有更好的實現方式。
最後總結一下 fetch 的三種情況
- pending: 請求中
- resolve: 請求成功(code 200: 呼叫 resolve 返回資料; code: 404/500 等, 先拋錯,再呼叫 resolve 返回資料。)
- reject: 請求失敗(跨域失敗、連線超時、無網路等,先控制檯拋錯,再呼叫 reject)
拋錯均不影響程式碼執行,與 undefined.a 不同。