本篇文章是Promise
系列文章的第二篇,主要是講解基於Promise/A+
規範,在傳入不一樣類型的參數時,promise
內部分別會如何處理。本章的主要目的是讓你們對promise
有一個更加深刻的理解,也爲下一篇講如何實現一個promise
庫作準備。(寫完以後以爲好水。。。)git
英文版本的規範見這裏,segmentfault上也有人把規範翻譯爲中文,見這裏。github
在此,我主要是經過使用例子,講解一下規範中then
方法和Promise Resolution Procedure
的每一種狀況。web
規範中對於構造函數沒有明確說明,因此在此處拿出來說解一下。segmentfault
和普通JavaScript對象同樣,咱們一樣是經過new關鍵詞來建立一個Promise
對象實例。構造函數只接收一個參數,且該參數必須是一個函數,任何其餘的值好比undefined
、null
、5
、true
等都會報一個TypeError
的錯誤。例:promise
new Promise(true) // Uncaught TypeError: Promise resolver true is not a function(…)
一樣,若是你沒有經過new關鍵詞建立,而是直接執行Promise(),一樣也會報一個TypeError
的錯誤。session
Promise() // Uncaught TypeError: undefined is not a promise(…)
因此,咱們必須經過new Promise(function()=>{})
的方式來建立一個Promise實例。一般咱們見到的建立一個Promise實例的代碼以下:異步
var promise = new Promise(function(resolve, reject) { // 進行一些異步操做 // 而後調用resolve或reject方法 });
這纔是正確的姿式~ 從該例子中,咱們能夠看到建立Promise實例時傳入的函數,同時還接受兩個參數,它們分別對應Promise內部實現的兩個方法。上一篇文章中,我提到過Promise有三種狀態,pending
、fulfilled
、rejected
,實例剛建立時處於pending
狀態,當執行reject
方法時,變爲rejected
狀態,以下所示:函數
new Promise(function(resolve, reject){ reject(Promise.resolve(5)) }).then(function(value){ console.log('fulfill', value) }, function(reason){ console.log('reject', reason) }) // reject Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 5}
而當執行resolve
方法時,它可能變爲fulfilled
,也有可能變爲rejected
狀態。也就是說resolve != fulfill
。以下:oop
new Promise(function(resolve, reject){ resolve(Promise.reject(5)) }).then(function(value){ console.log('fulfill', value) }, function(reason){ console.log('reject', reason) }) // reject 5
那麼resolve
是個什麼東西呢?它是根據什麼變爲fulfilled
或rejected
的呢?這就是咱們接下來要講解的Promise Resolution Procedure
,我把它稱做「Promise處理程序」。測試
講以前,咱們先說幾個promise
規範中的幾個術語。
promise 它是一個擁有then
方法的對象或函數,且符合該規範
thenable 擁有then
方法的對象或函數
value 是指一個合法的 Javascript
值
exception throw
語句拋出的異常
reason 描述promise爲何失敗的值
Promise Resolution Procedure
是對傳入的promise和value進行抽象操做。咱們可一個把它理解成resolve(promise, value)
,對參數promise和value進行一系列處理操做。下面咱們按照規範中的順序,依次介紹每種狀況。
2.3.1 若是promise
和value
指向同一個對象,則reject
該promise
並以一個TypeError
做爲reason
。
var defer = {} var promise = new Promise(function(resolve){ defer.resolve = resolve }) promise.catch(function(reason){ console.log(reason) }) defer.resolve(promise) // TypeError: Chaining cycle detected for promise #<Promise>(…)
咱們把resolve函數保存在defer中,這樣就能夠在外部對promise
進行狀態改變,defer.resolve(promise)
中的promise
正是咱們建立的對象,根據規範拋出了TypeError
。
2.3.2 若是value
是一個promise
對象,且是基於當前實現建立的。
2.3.2.1 若是value
處於pending
狀態,則promise
一樣pending
並直到value
狀態改變。
2.3.2.2 若是value
處於fulfilled
狀態,則使用相同的value值fulfill promise
。
2.3.2.3 若是value
處於rejected
狀態,則使用相同的reason值reject promise
。
var promise1 = new Promise((resolve) => { setTimeout(() => { resolve(5) },3000) }); console.time('fulfill') var promise = new Promise((resolve) => { resolve(promise1) }) promise.then((value) => { console.timeEnd('fulfill') console.log('fulfill', value) }) setTimeout(()=>{ console.log('setTimeout', promise) }, 1000) // setTimeout Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} // fulfill: 3.01e+03ms // fulfill 5
經過該例子能夠看出,最後setTimeout
延遲1秒查看promise
狀態時,它依然處於pending
狀態,當3秒後promise1
變爲fulfilled
後,promise
隨即變爲fulfilled
並以5做爲value傳給then
添加的成功回調函數中。
var promise1 = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('error')) }, 3000) }); console.time('reject') var promise = new Promise((resolve) => { resolve(promise1) }) promise.catch((reason) => { console.timeEnd('reject') console.log('reject', reason) }) setTimeout(()=>{ console.log('setTimeout', promise) }, 1000) // setTimeout Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} // reject: 3e+03ms // reject Error: error(…)
失敗時例子與成功時相似。
2.3.3 若是value
是一個對象或函數
2.3.3.1 使then
等於value.then
2.3.3.2 若是獲取value.then
的值時拋出異常,這經過該異常reject
promise
,例:
new Promise((resolve)=>{ resolve({then:(()=>{ throw new Error('error') })() }) }).catch((reason)=>{ console.log(reason) }) // Error: error(…)
上例中獲取value.then
時,會拋出異常
2.3.3.3 若是then
是一個函數,則把value
做爲函數中this
指向來調用它,第一個參數是resolvePromise
,第二個參數是rejectPromise
。
其實這裏主要是爲了兼容兩種狀況,第一種是傳入的value
是個Deferred
對象,則狀態和Deferred
對象一致;另外一種狀況是否是使用當前構造函數建立的Promise
對象,經過這種方式能夠兼容,達到一致的效果。
2.3.3.3.1 若是resolvePromise
經過傳入y
來調用,則執行resolve(promise, y)
,例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ resolvePromise(5) } }) }).then((value)=>{ console.log(value) }) // 5
2.3.3.3.2 若是rejectPromise
經過傳入緣由r
來調用,則傳入r
來reject
promise
,例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ rejectPromise(new Error('error')) } }) }).catch((reason)=>{ console.log(reason) }) // Error: error(…)
2.3.3.3.3 若是resolvePromise
和rejectPromise
都被調用,或其中一個被調用了屢次,則以第一次調用的爲準,並忽略以後的調用。例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ resolvePromise(5) rejectPromise(new Error('error')) } }) }).then((value)=>{ console.log(value) }, (reason)=>{ console.log(reason) }) // 5
2.3.3.3.4 若是調用then
拋出異常e
:
2.3.3.3.4.1 若是resolvePromise
或rejectPromise
已經調用,則忽略它,例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ resolvePromise(5) throw new Error('error') } }) }).then((value)=>{ console.log(value) }, (reason)=>{ console.log(reason) }) // 5
2.3.3.3.4.2 不然,則傳入e
來reject
promise
,例:
new Promise((resolve)=>{ resolve({then:(resolvePromise, rejectPromise)=>{ throw new Error('error') } }) }).then((value)=>{ console.log(value) }, (reason)=>{ console.log(reason) }) // Error: error(…)
2.3.3.4 若是then
不是一個函數,則傳入value
來fulfill
promise
,例:
new Promise((resolve)=>{ resolve({then:5}) }).then((value)=>{ console.log(value) }, (reason)=>{ console.log(reason) }) // Object {then: 5}
then
方法一個promise
必須提供一個then
方法來處理成功或失敗。
then
方法接收兩個參數:
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled
和onRejected
都是可選的
2.2.1.1 若是onFulfilled
不是一個函數,則忽略。例:
Promise.resolve(5) .then(true,function(reason){ console.log(reason) }) .then(function(value){ console.log(value) }) // 5
2.2.1.2 若是onRejected
不是一個函數,則忽略。例:
Promise.reject(new Error('error')) .then(true,null) .then(undefined,function(reason){ console.log(reason) }) // Error: error(…)
2.2.2 若是onFulfilled
是一個函數
2.2.2.1 它必須在promise
變爲fulfilled
以後調用,且把promise
的value
做爲它的第一個參數
這個從咱們全部的例子中均可以看出
2.2.2.2 它不能夠在promise
變爲fulfilled
以前調用
var defer = {} console.time('fulfill') var promise = new Promise((resolve)=>{ defer.resolve = resolve }); promise.then((value)=>{ console.timeEnd('fulfill') }) setTimeout(()=>{ defer.resolve(5) },1000); // fulfill: 1e+03ms
從onFulfilled
執行的時間能夠看出promise
直到變爲fulfilled
後才調用
2.2.2.3 它只能夠被調用一次
var defer = {} var promise = new Promise((resolve)=>{ defer.resolve = resolve }); promise.then((value)=>{ console.log(value++) }) defer.resolve(5) // 5 defer.resolve(6) // 後面再也不次執行
2.2.3 若是onRejected
是一個函數
2.2.3.1 它必須在promise
變爲rejected
以後調用,且把promise
的reason
做爲它的第一個參數
2.2.3.2 它不能夠在promise
變爲rejected
以前調用
2.2.3.3 它只能夠被調用一次
onRejected
和onFulfilled
基本相似,這裏再也不次贅述
2.2.4 onFulfilled
和onRejected
是在執行環境中僅包含平臺代碼時調用
這裏有一個備註,平臺代碼是指引擎、執行環境、以及promise
的實現代碼。實際過程當中,要確保onFulfilled
和onRejected
是異步執行的,它是在event loop
過程當中then
方法被調用以後的新調用棧中執行。咱們可使用setTimeout
或setImmediate
等macro-task
機制來實現,也可使用MutationObserver
或process.nextTick
等micro-task
機制來實現。promise
的實現自己就被看做是平臺代碼,它自己就包含一個處理器能夠調用的任務調度隊列。
才疏學淺,沒理解它這一條到底要表達一個什麼意思。。。應該指的就是異步執行,由於異步執行的時候,頁面中同步的邏輯都已經執行完畢,因此只剩下平臺代碼。
注:原生的Promise
實現屬於micro-task
機制。macro-task
和micro-task
分別是兩種異步任務,它們的不一樣後面會單獨講一下。下面列出了常見的異步方法都屬於那種異步機制:
macro-task: script(總體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-task: process.nextTick, 原生Promise, Object.observe, MutationObserver
2.2.5 onFulfilled
和onRejected
必須做爲函數來調用,沒有this
值
Promise.resolve(5).then(function(){ console.log(this) }) // Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
2.2.6 同一個promise
上的then
方法可能會調用屢次
2.2.6.1 若是promise
fulfilled
,則全部的onFulfilled
回調函數按照它們添加的順序依次調用。
var defer = {} var promise = new Promise((resolve)=>{ defer.resolve = resolve }); promise.then((value)=>{ console.log(1,value++) }) promise.then((value)=>{ console.log(2,value++) }) promise.then((value)=>{ console.log(3,value++) }) defer.resolve(5) // 1 5 // 2 5 // 3 5
2.2.6.2 若是promise
rejected
,則全部的onRejected
回調函數按照它們添加的順序依次調用。
例子與上例相似
2.2.7 then
方法會返回一個全新的promise
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 若是onFulfilled
或onRejected
返回了一個值x
,則執行resolve(promise2, x)
Promise.resolve(5).then(function(value){ return ++value }).then(function(value){ console.log(value) }) // 6
2.2.7.2 若是onFulfilled
或onRejected
拋出了異常e
,則reject
promise2
並傳入緣由e
Promise.resolve(5).then(function(value){ throw new Error('error') }).catch(function(reason){ console.log(reason) }) // Error: error(…)
2.2.7.3 若是onFulfilled
不是一個函數且promise1
fulfilled
,則promise2
以一樣的value
fulfill
Promise.resolve(5).then("tiaoguo").then(function(value){ console.log(value) }) // 5
2.2.7.4 若是onRejected
不是一個函數且promise1
rejected
,則promise2
以一樣的reason
reject
Promise.reject(new Error('error')).catch('tiaoguo').catch(function(reason){ console.log(reason) }) // Error: error(…)
更多的測試代碼,你們能夠去promises-tests中查看,這是一個基於規範的promise
測試庫。
以上基本是整個Promise/A+
規範的全部內容,若有錯誤,歡迎批評指正。下一篇我會根據規範一步一步實現一個Promise
polyfill
庫。