摘要:規范中對于構造函數沒有明確說明,所以在此處拿出來講解一下。構造函數只接收一個參數,且該參數必須是一個函數,任何其他的值比如等都會報一個的錯誤。
本篇文章是Promise系列文章的第二篇,主要是講解基于Promise/A+規范,在傳入不同類型的參數時,promise內部分別會如何處理。本章的主要目的是讓大家對promise有一個更加深入的理解,也為下一篇講如何實現一個promise庫做準備。(寫完之后覺得好水。。。)
英文版本的規范見這里,segmentfault上也有人把規范翻譯為中文,見這里。
在此,我主要是通過使用例子,講解一下規范中then方法和Promise Resolution Procedure的每一種情況。
constructor規范中對于構造函數沒有明確說明,所以在此處拿出來講解一下。
和普通JavaScript對象一樣,我們同樣是通過new關鍵詞來創建一個Promise對象實例。構造函數只接收一個參數,且該參數必須是一個函數,任何其他的值比如undefined、null、5、true等都會報一個TypeError的錯誤。例:
new Promise(true) // Uncaught TypeError: Promise resolver true is not a function(…)
同樣,如果你沒有通過new關鍵詞創建,而是直接執行Promise(),同樣也會報一個TypeError的錯誤。
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。如下:
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 Resolution Procedure講之前,我們先說幾個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 #(…)
我們把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庫。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88042.html
摘要:我們稱為回調對象,它內部會維護一個數組,我們可以向其中添加若干個回調函數,然后在某一條件下觸發執行。第一次之后,再次新的回調函數時,自動執行回調。當前面的回調函數返回時,終止后面的回調繼續執行。 最近懶癌發作,說好的系列文章,寫了一半,一直懶得寫,今天補上一篇。 Deferred 我們在使用promise對象時,總會提到一個與它關系密切的對象——Deferred。其實Deferred沒...
摘要:的翻譯文檔由的維護很多人說,阮老師已經有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:內部總體上分為兩種情況,一種是當前對象狀態已經變為或,此時則直接把響應的回調函數添加到異步隊列中,另一種情況是當前對象狀態還是,此時則把響應的回調函數依次添加到數組中。 今天,我帶著大家一步一步跟著規范實現一個自己的Promise,大家可以對照我的第二篇文章Promise介紹--規范篇或官方規范來一一學習。 Promise內部有三個固定的狀態,我們在文件中提前定義。 const PEN...
摘要:因此,當作為參數的執行任意結果的回調函數時,就會將參數傳遞給外層的,執行對應的回調函數。 背景 在上一篇博客[[譯]前端基礎知識儲備——Promise/A+規范](https://segmentfault.com/a/11...,我們介紹了Promise/A+規范的具體條目。在本文中,我們來選擇了promiz,讓大家來看下一個具體的Promise庫的內部代碼是如何運作的。 promiz...
摘要:事件循環持續運行,直到清空列隊的任務。在執行期間,瀏覽器可能更新渲染。線索可能會發生多次。由于冒泡,函數再一次執行。這意味著隊列不會在事件回調之間處理,而是在它們之后處理。當觸發成功事件時,相關的對象在事件之后轉為非激活狀態第四步。 一 前言 一直想對異步處理做一個研究,在查閱資料時發現了這篇文章,非常深入的解釋了事件循環中重的任務隊列。原文中有代碼執行工具,強烈建議自己執行一下查看結...
閱讀 1695·2021-11-24 09:39
閱讀 2469·2021-11-18 10:07
閱讀 3657·2021-08-31 09:40
閱讀 3317·2019-08-30 15:44
閱讀 2628·2019-08-30 12:50
閱讀 3649·2019-08-26 17:04
閱讀 1430·2019-08-26 13:49
閱讀 1262·2019-08-23 18:05