摘要:本文僅限瀏覽器環境測試,環境可能會不一致狀態一個實例只能處于三種狀態中的一種。每次創建的實例都會處于狀態,并且只能由變為或狀態。可以認為在實現里與中的都為解決程序。
前言
Promise作為ES6極為重要的一個特性,將我們從無限的回調地獄中解脫出來,變為鏈式的編寫回調,大大提高的代碼的可讀性。
使用Promise是極為簡單的,但只停留在會使用階段還是會讓我們不知不覺踩到一些坑的。本文會結合Promise/A+規范與示例來深入學習Promise
本文較長,例子很多,末尾有一些應用 ^_^
Promise兼容性
Promise作為是瀏覽器的內置函數對象,除IE不支持外所有主流版本的瀏覽器都是支持的,如不需支持IE可以在不引入polyfill的情況下放心食用
作用
js是單線程的,異步是通過Event Loop來實現的,那么需要一種更加友好的方式來實現我們的異步操作,而不是無限的回調嵌套
// no promise callback(() => { callback(() => { callback(() => { ... }) }) }) // with promise new Promise((resolve, reject) => { }).then(() => { }).then(() => { }).then(() => { })
API
先來簡單看一下Promise提供的一些API(注意區分靜態方法與實例方法)
構造函數Promise
Promise是一個構造函數,可以通過new操作符來創建一個Promise實例
let promise = new Promise((resolve, reject) => { resolve(1) })
靜態方法
Promise.resolve
Promise.reject
Promise.all
Promise.race
實例方法
Promise.prototype.then
Promise.prototype.catch
結合規范與樣例
規范
官方英文原版規范
tips:
規范是規范,實現是實現,實現是按照規范來實現的,但不一定完全等同于規范。
本文僅限瀏覽器環境測試,node環境可能會不一致
狀態
一個Promise實例只能處于pending,fulfilled,rejected三種狀態中的一種。每次創建的promise實例都會處于pending狀態,并且只能由pending變為fulfilled或reject狀態。一旦處于fulfilled或rejected狀態無論進行什么操作都不會再次變更狀態(規范2.1)
// 創建一個處于pending狀態的promise實例 let promise1 = new Promise((resolve, reject) => { }) // 調用resolve()會使promise從pending狀態變為fulfilled狀態 let promise2 = new Promise((resolve, reject) => { resolve(1) }) // 調用reject()會使promise從pending狀態變為rejected狀態 let promise3 = new Promise((resolve, reject) => { reject(1) }) // 無論如何更改resolve與reject的執行順序,promise4始終只會處于先調用后轉換的狀態(狀態一旦變為fulfilled或rejected后不會再次改變) let promise4 = new Promise((resolve, reject) => { resolve(1) reject(1) })
then
參數
實例方法then的回調函數接受兩個參數,onFulfilled和onRejected,都為可選參數(規范2.2.1)
// onFulfilled會在狀態變為fulfilled后調用,onFulfilled參數value為resolve的第一個參數,onRejected同理。(規范2.2.2與2.2.3) let promise = new Promise((resolve, reject) => { setTimeout(() => { resolve(1) }, 1000) }) promise.then((value) => { console.log(value) // 1秒后輸出1 }, (reason) => { }) // 可被同一個promise多次調用(規范2.2.6) promise.then() promise.then((value) => { console.log(value) // 1秒后輸出1 })
返回值
then方法必須返回一個promise實例(規范2.2.7)
假定 promise2 = promise1.then(onFulfilled, onRejected)
var a = new Promise((resolve, reject) => { resolve(1) }) var b = new Promise((resolve, reject) => { reject(1) }) // 如果```onFulfilled```或```onRejected```返回了一個value ```x```,運行promise解決程序(下一節), [[Resolve]](promise2, x) (規范2.2.7.1) var a1 = a.then(function onFulfilled(value) { return 1 }, function onRejected (reason) { }) var b1 = b.then(function onFulfilled(value) { }, function onRejected (reason) { return 1 }) // 如果```onFulfilled```或```onRejected```拋出了一個exception ```e```, ```promise2```必須以e作為reason rejected (規范2.2.7.2) // 故下方a2 b2 都為狀態是rejected的promise實例 var a2 = a.then(function onFulfilled(value) { throw Error("test") }, function onRejected (reason) { }) var b2 = b.then(function onFulfilled(value) { }, function onRejected (reason) { throw Error("test") }) // 如果promise1處于fulfilled狀態并且onFulfilled不是一個函數,那么promise2會以與promise1具有相同value和相同的狀態, 但是與promise1并不是同一Promise實例;若為rejected狀態以此類推 var a3 = a.then() a3 === a // false var b3 = b.then() b3 === b // false
解決程序resolve
在規范中, promise解決程序是一個以promise和value作為輸入的抽象操作,我們表示為[[Resolve]](promise, x)。
可以認為在實現里Promise.resolve()與new Promise((resolve, reject) => {})中的resolve都為解決程序。規范中x對應實現中resolve的第一個參數,規范中promise在實現中等價于當前promise
可以理解為Promise.resolve(x)與new Promise(resolve) => {resolve(x)}等價
var c = new Promise((resolve, reject) => { resolve(1) }) var d = new Promise((resolve, reject) => { reject(1) }) var e = new Promise((resolve, reject) => { setTimeout(() => { resolve("5s后resolve") }, 5000) }) // 在返回值章節我們了解到,onFulfilled如果為函數在不拋出錯誤的情況下,會調用解決程序處理返回值x // 如果x是promise, 采納他的狀態(注意,但then的返回值并不與x相等)(規范2.3.2) var c1 = Promise.resolve().then(function onFulfilled() { return c }) c1 === c // false // pending狀態下的,只要e狀態變為,e1也會改變(規范2.3.2.1) var e1 = Promise.resolve().then(function onFulfilled () { return e }) var c2 = Promise.resolve() // 如果promise和x是相同的對象, 使用TypeError作為reason reject promise(規范2.3.1) var c3 = c2.then(function onFulfilled () { return c3 })
概念:如果一個函數或者對象具有then屬性,那么叫做thenable(例: { then: function(){} })
// 如果x是thenable 但x.then不是一個函數或者對象,直接返回狀態為fulfilled,value為x的promise實例(規范2.3.3.4) var c4 = c1.then(function onFulfilled () { return {} }) var c5 = c1.then(function onFulfilled () { return { then: 2 } }) // 如果x是thenable 并且x.then是一個函數,那么就會調用這個then方法執行,并且兩個參數resolve與reject擁有改變返回的promise狀態的權利,會按解決程序處理,如果不調用兩個參數方法,則返回的promise為pending狀態,值得一提的是,調用then方法的時候回以返回對象x作為then的this綁定調用(規范 2.3.3.3) var c6 = c1.then(function onFulfilled () { return { test: "test", then: function (resolve, reject) { console.log(this.test) resolve("thenable") } } }) var c7 = c1.then(function onFulfilled () { function x () {} x.test = "test" x.then = function (resolve, reject) { console.log(this.test) resolve("thenable") } return x }) var c8 = c1.then(function onFulfilled () { return { test: "test", then: function (resolve, reject) { console.log(this.test) reject("thenable") } } }) // 返回pending狀態的promise var c9 = c1.then(function onFulfilled () { return { then: function () {} } }) // 如果x不是對象也不是函數,則返回狀態為fulfilled的promise,value為x var c10 = c1.then(function onFulfilled () { return "hehe" })
綜上可以總結幾點
1. ```new Promise, promise.then, Promise.resolve()```都會返回promise實例 2. Promise狀態一旦改變不可再更改 3. ```then```方法返回對象的不同會導致不同的結果,如果意外返回了thenable有可能會造成意想不到的結果應用
封裝一個異步請求
var promiseXHR = new Promise((resolve, reject) => { var xhr = new XMLHttpRequest() xhr.open("GET", "http://www.baidu.com", true) xhr.onreadystatechange = function () { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { resolve(xhr.response) } else { reject() } } xhr.send(data) })
同時請求按序處理
// 假設有5個章節的數據,我們需要分別獲取并讀取到c中,利用promise和es6數組api我們可以寫出簡潔優雅的代碼完成這個需求 var list = ["Chapter1", "Chapter2", "Chapter3", "Chapter4", "Chapter5"] var getData = function (key) { return new Promise((resolve, reject) => { // 模擬一些返回時間不相同的異步操作 setTimeout(() => { resolve(key) }, 4000 * Math.random()) }) } var c = "" list.map(i => getData(i)) .reduce((accumulator, current) => { console.log(accumulator) return accumulator.then(() => { return current }).then(value => { c+= value }) }, Promise.resolve(""))
catch
明明在我們日常工作中常用到的catch方法,為什么到現在還一點都沒有提到呢?
因為catch方法就是then方法封裝出來的語法糖而已,因為如果只想捕獲錯誤,還每次都要書寫then的第一個參數,真的是麻煩至極,現在我們來寫一個自己的catch
Promise.prototype.mycatch = function (cb) { return this.then(1, function onRejected (reason) { return cb(reason) }) }
// 用到了規范中如果onFulfilled不是一個函數,則忽略,并使用原先promise狀態與value
finally
有的瀏覽器實現了finally,而有的并沒有,從需求上來講finally只不過是最終要執行一個函數,我們需要把應該有的狀態或者異常都繼續傳遞,不受其影響。執行的函數與promise的value無任何關系
Promise.prototype.myfinally = function (cb) { return this.then(function onFulfilled (value) { return Promise.resolve(cb()).then(() => value) }, function onRejected (reason) { return Promise.resolve(cb()).then(() => { throw reason }) }) }后記
通過閱讀規范并寫demo進行驗證測試,對Promise的理解更加深入了,也能更好的使用它了
如果喜歡可以star一下,會不斷更新github地址
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/94387.html
摘要:感謝大神的免費的計算機編程類中文書籍收錄并推薦地址,以后在倉庫里更新地址,聲音版全文狼叔如何正確的學習簡介現在,越來越多的科技公司和開發者開始使用開發各種應用。 說明 2017-12-14 我發了一篇文章《沒用過Node.js,就別瞎逼逼》是因為有人在知乎上黑Node.js。那篇文章的反響還是相當不錯的,甚至連著名的hax賀老都很認同,下班時讀那篇文章,竟然坐車的還坐過站了。大家可以很...
摘要:感謝大神的免費的計算機編程類中文書籍收錄并推薦地址,以后在倉庫里更新地址,聲音版全文狼叔如何正確的學習簡介現在,越來越多的科技公司和開發者開始使用開發各種應用。 說明 2017-12-14 我發了一篇文章《沒用過Node.js,就別瞎逼逼》是因為有人在知乎上黑Node.js。那篇文章的反響還是相當不錯的,甚至連著名的hax賀老都很認同,下班時讀那篇文章,竟然坐車的還坐過站了。大家可以很...
摘要:在他的重學前端課程中提到到現在為止,前端工程師已經成為研發體系中的重要崗位之一。大部分前端工程師的知識,其實都是來自于實踐和工作中零散的學習。一基礎前端工程師吃飯的家伙,深度廣度一樣都不能差。 開篇 前端開發是一個非常特殊的行業,它的歷史實際上不是很長,但是知識之繁雜,技術迭代速度之快是其他技術所不能比擬的。 winter在他的《重學前端》課程中提到: 到現在為止,前端工程師已經成為研...
摘要:在他的重學前端課程中提到到現在為止,前端工程師已經成為研發體系中的重要崗位之一。大部分前端工程師的知識,其實都是來自于實踐和工作中零散的學習。一基礎前端工程師吃飯的家伙,深度廣度一樣都不能差。開篇 前端開發是一個非常特殊的行業,它的歷史實際上不是很長,但是知識之繁雜,技術迭代速度之快是其他技術所不能比擬的。 winter在他的《重學前端》課程中提到: 到現在為止,前端工程師已經成為研發體系...
閱讀 945·2021-09-07 09:58
閱讀 1495·2021-09-07 09:58
閱讀 2890·2021-09-04 16:40
閱讀 2510·2019-08-30 15:55
閱讀 2416·2019-08-30 15:54
閱讀 1375·2019-08-30 15:52
閱讀 439·2019-08-30 10:49
閱讀 2610·2019-08-29 13:21