摘要:規(guī)范中對(duì)于構(gòu)造函數(shù)沒有明確說(shuō)明,所以在此處拿出來(lái)講解一下。構(gòu)造函數(shù)只接收一個(gè)參數(shù),且該參數(shù)必須是一個(gè)函數(shù),任何其他的值比如等都會(huì)報(bào)一個(gè)的錯(cuò)誤。
本篇文章是Promise系列文章的第二篇,主要是講解基于Promise/A+規(guī)范,在傳入不同類型的參數(shù)時(shí),promise內(nèi)部分別會(huì)如何處理。本章的主要目的是讓大家對(duì)promise有一個(gè)更加深入的理解,也為下一篇講如何實(shí)現(xiàn)一個(gè)promise庫(kù)做準(zhǔn)備。(寫完之后覺得好水。。。)
英文版本的規(guī)范見這里,segmentfault上也有人把規(guī)范翻譯為中文,見這里。
在此,我主要是通過使用例子,講解一下規(guī)范中then方法和Promise Resolution Procedure的每一種情況。
constructor規(guī)范中對(duì)于構(gòu)造函數(shù)沒有明確說(shuō)明,所以在此處拿出來(lái)講解一下。
和普通JavaScript對(duì)象一樣,我們同樣是通過new關(guān)鍵詞來(lái)創(chuàng)建一個(gè)Promise對(duì)象實(shí)例。構(gòu)造函數(shù)只接收一個(gè)參數(shù),且該參數(shù)必須是一個(gè)函數(shù),任何其他的值比如undefined、null、5、true等都會(huì)報(bào)一個(gè)TypeError的錯(cuò)誤。例:
new Promise(true) // Uncaught TypeError: Promise resolver true is not a function(…)
同樣,如果你沒有通過new關(guān)鍵詞創(chuàng)建,而是直接執(zhí)行Promise(),同樣也會(huì)報(bào)一個(gè)TypeError的錯(cuò)誤。
Promise() // Uncaught TypeError: undefined is not a promise(…)
所以,我們必須通過new Promise(function()=>{})的方式來(lái)創(chuàng)建一個(gè)Promise實(shí)例。通常我們見到的創(chuàng)建一個(gè)Promise實(shí)例的代碼如下:
var promise = new Promise(function(resolve, reject) { // 進(jìn)行一些異步操作 // 然后調(diào)用resolve或reject方法 });
這才是正確的姿勢(shì)~ 從該例子中,我們可以看到創(chuàng)建Promise實(shí)例時(shí)傳入的函數(shù),同時(shí)還接受兩個(gè)參數(shù),它們分別對(duì)應(yīng)Promise內(nèi)部實(shí)現(xiàn)的兩個(gè)方法。上一篇文章中,我提到過Promise有三種狀態(tài),pending、fulfilled、rejected,實(shí)例剛創(chuàng)建時(shí)處于pending狀態(tài),當(dāng)執(zhí)行reject方法時(shí),變?yōu)?b>rejected狀態(tài),如下所示:
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}
而當(dāng)執(zhí)行resolve方法時(shí),它可能變?yōu)?b>fulfilled,也有可能變?yōu)?b>rejected狀態(tài)。也就是說(shuō)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是個(gè)什么東西呢?它是根據(jù)什么變?yōu)?b>fulfilled或rejected的呢?這就是我們接下來(lái)要講解的Promise Resolution Procedure,我把它稱作“Promise處理程序”。
Promise Resolution Procedure講之前,我們先說(shuō)幾個(gè)promise規(guī)范中的幾個(gè)術(shù)語(yǔ)。
promise 它是一個(gè)擁有then方法的對(duì)象或函數(shù),且符合該規(guī)范
thenable 擁有then方法的對(duì)象或函數(shù)
value 是指一個(gè)合法的 Javascript 值
exception throw語(yǔ)句拋出的異常
reason 描述promise為什么失敗的值
Promise Resolution Procedure是對(duì)傳入的promise和value進(jìn)行抽象操作。我們可一個(gè)把它理解成resolve(promise, value),對(duì)參數(shù)promise和value進(jìn)行一系列處理操作。下面我們按照規(guī)范中的順序,依次介紹每種情況。
2.3.1 如果promise和value指向同一個(gè)對(duì)象,則reject該promise并以一個(gè)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函數(shù)保存在defer中,這樣就可以在外部對(duì)promise進(jìn)行狀態(tài)改變,defer.resolve(promise)中的promise正是我們創(chuàng)建的對(duì)象,根據(jù)規(guī)范拋出了TypeError。
2.3.2 如果value是一個(gè)promise對(duì)象,且是基于當(dāng)前實(shí)現(xiàn)創(chuàng)建的。
2.3.2.1 如果value處于pending狀態(tài),則promise同樣pending并直到value狀態(tài)改變。
2.3.2.2 如果value處于fulfilled狀態(tài),則使用相同的value值fulfill promise。
2.3.2.3 如果value處于rejected狀態(tài),則使用相同的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狀態(tài)時(shí),它依然處于pending狀態(tài),當(dāng)3秒后promise1變?yōu)?b>fulfilled后,promise隨即變?yōu)?b>fulfilled并以5作為value傳給then添加的成功回調(diào)函數(shù)中。
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(…)
失敗時(shí)例子與成功時(shí)類似。
2.3.3 如果value是一個(gè)對(duì)象或函數(shù)
2.3.3.1 使then等于value.then
2.3.3.2 如果獲取value.then的值時(shí)拋出異常,這通過該異常reject promise,例:
new Promise((resolve)=>{ resolve({then:(()=>{ throw new Error("error") })() }) }).catch((reason)=>{ console.log(reason) }) // Error: error(…)
上例中獲取value.then時(shí),會(huì)拋出異常
2.3.3.3 如果then是一個(gè)函數(shù),則把value作為函數(shù)中this指向來(lái)調(diào)用它,第一個(gè)參數(shù)是resolvePromise,第二個(gè)參數(shù)是rejectPromise。
其實(shí)這里主要是為了兼容兩種情況,第一種是傳入的value是個(gè)Deferred對(duì)象,則狀態(tài)和Deferred對(duì)象一致;另一種情況是不是使用當(dāng)前構(gòu)造函數(shù)創(chuàng)建的Promise對(duì)象,通過這種方式可以兼容,達(dá)到一致的效果。
2.3.3.3.1 如果resolvePromise通過傳入y來(lái)調(diào)用,則執(zhí)行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來(lái)調(diào)用,則傳入r來(lái)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都被調(diào)用,或其中一個(gè)被調(diào)用了多次,則以第一次調(diào)用的為準(zhǔn),并忽略之后的調(diào)用。例:
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 如果調(diào)用then拋出異常e:
2.3.3.3.4.1 如果resolvePromise或rejectPromise已經(jīng)調(diào)用,則忽略它,例:
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來(lái)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不是一個(gè)函數(shù),則傳入value來(lái)fulfill promise,例:
new Promise((resolve)=>{ resolve({then:5}) }).then((value)=>{ console.log(value) }, (reason)=>{ console.log(reason) }) // Object {then: 5}then 方法
一個(gè)promise必須提供一個(gè)then方法來(lái)處理成功或失敗。
then方法接收兩個(gè)參數(shù):
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled和onRejected都是可選的
2.2.1.1 如果onFulfilled不是一個(gè)函數(shù),則忽略。例:
Promise.resolve(5) .then(true,function(reason){ console.log(reason) }) .then(function(value){ console.log(value) }) // 5
2.2.1.2 如果onRejected不是一個(gè)函數(shù),則忽略。例:
Promise.reject(new Error("error")) .then(true,null) .then(undefined,function(reason){ console.log(reason) }) // Error: error(…)
2.2.2 如果onFulfilled是一個(gè)函數(shù)
2.2.2.1 它必須在promise變?yōu)?b>fulfilled之后調(diào)用,且把promise的value作為它的第一個(gè)參數(shù)
這個(gè)從我們所有的例子中都可以看出
2.2.2.2 它不可以在promise變?yōu)?b>fulfilled之前調(diào)用
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執(zhí)行的時(shí)間可以看出promise直到變?yōu)?b>fulfilled后才調(diào)用
2.2.2.3 它只可以被調(diào)用一次
var defer = {} var promise = new Promise((resolve)=>{ defer.resolve = resolve }); promise.then((value)=>{ console.log(value++) }) defer.resolve(5) // 5 defer.resolve(6) // 后面不再次執(zhí)行
2.2.3 如果onRejected是一個(gè)函數(shù)
2.2.3.1 它必須在promise變?yōu)?b>rejected之后調(diào)用,且把promise的reason作為它的第一個(gè)參數(shù)
2.2.3.2 它不可以在promise變?yōu)?b>rejected之前調(diào)用
2.2.3.3 它只可以被調(diào)用一次
onRejected和onFulfilled基本類似,這里不再次贅述
2.2.4 onFulfilled和onRejected是在執(zhí)行環(huán)境中僅包含平臺(tái)代碼時(shí)調(diào)用
這里有一個(gè)備注,平臺(tái)代碼是指引擎、執(zhí)行環(huán)境、以及promise的實(shí)現(xiàn)代碼。實(shí)際過程中,要確保onFulfilled和onRejected是異步執(zhí)行的,它是在event loop過程中then方法被調(diào)用之后的新調(diào)用棧中執(zhí)行。我們可以使用setTimeout或setImmediate等macro-task機(jī)制來(lái)實(shí)現(xiàn),也可以使用MutationObserver或process.nextTick等micro-task機(jī)制來(lái)實(shí)現(xiàn)。promise的實(shí)現(xiàn)本身就被看作是平臺(tái)代碼,它本身就包含一個(gè)處理器可以調(diào)用的任務(wù)調(diào)度隊(duì)列。
才疏學(xué)淺,沒理解它這一條到底要表達(dá)一個(gè)什么意思。。。應(yīng)該指的就是異步執(zhí)行,因?yàn)楫惒綀?zhí)行的時(shí)候,頁(yè)面中同步的邏輯都已經(jīng)執(zhí)行完畢,所以只剩下平臺(tái)代碼。
注:原生的Promise實(shí)現(xiàn)屬于micro-task機(jī)制。macro-task和micro-task分別是兩種異步任務(wù),它們的不同后面會(huì)多帶帶講一下。下面列出了常見的異步方法都屬于那種異步機(jī)制:
macro-task: script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-task: process.nextTick, 原生Promise, Object.observe, MutationObserver
2.2.5 onFulfilled和onRejected必須作為函數(shù)來(lái)調(diào)用,沒有this值
Promise.resolve(5).then(function(){ console.log(this) }) // Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage, sessionStorage: Storage, webkitStorageInfo: DeprecatedStorageInfo…}
2.2.6 同一個(gè)promise上的then方法可能會(huì)調(diào)用多次
2.2.6.1 如果promise fulfilled,則所有的onFulfilled回調(diào)函數(shù)按照它們添加的順序依次調(diào)用。
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回調(diào)函數(shù)按照它們添加的順序依次調(diào)用。
例子與上例類似
2.2.7 then方法會(huì)返回一個(gè)全新的promise
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 如果onFulfilled或onRejected返回了一個(gè)值x,則執(zhí)行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不是一個(gè)函數(shù)且promise1 fulfilled,則promise2以同樣的value fulfill
Promise.resolve(5).then("tiaoguo").then(function(value){ console.log(value) }) // 5
2.2.7.4 如果onRejected不是一個(gè)函數(shù)且promise1 rejected,則promise2以同樣的reason reject
Promise.reject(new Error("error")).catch("tiaoguo").catch(function(reason){ console.log(reason) }) // Error: error(…)
更多的測(cè)試代碼,大家可以去promises-tests中查看,這是一個(gè)基于規(guī)范的promise測(cè)試庫(kù)。
以上基本是整個(gè)Promise/A+規(guī)范的所有內(nèi)容,如有錯(cuò)誤,歡迎批評(píng)指正。下一篇我會(huì)根據(jù)規(guī)范一步一步實(shí)現(xiàn)一個(gè)Promise polyfill庫(kù)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88042.html
摘要:我們稱為回調(diào)對(duì)象,它內(nèi)部會(huì)維護(hù)一個(gè)數(shù)組,我們可以向其中添加若干個(gè)回調(diào)函數(shù),然后在某一條件下觸發(fā)執(zhí)行。第一次之后,再次新的回調(diào)函數(shù)時(shí),自動(dòng)執(zhí)行回調(diào)。當(dāng)前面的回調(diào)函數(shù)返回時(shí),終止后面的回調(diào)繼續(xù)執(zhí)行。 最近懶癌發(fā)作,說(shuō)好的系列文章,寫了一半,一直懶得寫,今天補(bǔ)上一篇。 Deferred 我們?cè)谑褂胮romise對(duì)象時(shí),總會(huì)提到一個(gè)與它關(guān)系密切的對(duì)象——Deferred。其實(shí)Deferred沒...
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:內(nèi)部總體上分為兩種情況,一種是當(dāng)前對(duì)象狀態(tài)已經(jīng)變?yōu)榛?,此時(shí)則直接把響應(yīng)的回調(diào)函數(shù)添加到異步隊(duì)列中,另一種情況是當(dāng)前對(duì)象狀態(tài)還是,此時(shí)則把響應(yīng)的回調(diào)函數(shù)依次添加到數(shù)組中。 今天,我?guī)е蠹乙徊揭徊礁?guī)范實(shí)現(xiàn)一個(gè)自己的Promise,大家可以對(duì)照我的第二篇文章Promise介紹--規(guī)范篇或官方規(guī)范來(lái)一一學(xué)習(xí)。 Promise內(nèi)部有三個(gè)固定的狀態(tài),我們?cè)谖募刑崆岸x。 const PEN...
摘要:因此,當(dāng)作為參數(shù)的執(zhí)行任意結(jié)果的回調(diào)函數(shù)時(shí),就會(huì)將參數(shù)傳遞給外層的,執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)。 背景 在上一篇博客[[譯]前端基礎(chǔ)知識(shí)儲(chǔ)備——Promise/A+規(guī)范](https://segmentfault.com/a/11...,我們介紹了Promise/A+規(guī)范的具體條目。在本文中,我們來(lái)選擇了promiz,讓大家來(lái)看下一個(gè)具體的Promise庫(kù)的內(nèi)部代碼是如何運(yùn)作的。 promiz...
摘要:事件循環(huán)持續(xù)運(yùn)行,直到清空列隊(duì)的任務(wù)。在執(zhí)行期間,瀏覽器可能更新渲染。線索可能會(huì)發(fā)生多次。由于冒泡,函數(shù)再一次執(zhí)行。這意味著隊(duì)列不會(huì)在事件回調(diào)之間處理,而是在它們之后處理。當(dāng)觸發(fā)成功事件時(shí),相關(guān)的對(duì)象在事件之后轉(zhuǎn)為非激活狀態(tài)第四步。 一 前言 一直想對(duì)異步處理做一個(gè)研究,在查閱資料時(shí)發(fā)現(xiàn)了這篇文章,非常深入的解釋了事件循環(huán)中重的任務(wù)隊(duì)列。原文中有代碼執(zhí)行工具,強(qiáng)烈建議自己執(zhí)行一下查看結(jié)...
閱讀 1711·2021-11-24 09:39
閱讀 2493·2021-11-18 10:07
閱讀 3675·2021-08-31 09:40
閱讀 3346·2019-08-30 15:44
閱讀 2641·2019-08-30 12:50
閱讀 3661·2019-08-26 17:04
閱讀 1438·2019-08-26 13:49
閱讀 1273·2019-08-23 18:05