本篇文章是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 #<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
库。