Promise介绍--规范篇

本篇文章是Promise系列文章的第二篇,主要是讲解基于Promise/A+规范,在传入不一样类型的参数时,promise内部分别会如何处理。本章的主要目的是让你们对promise有一个更加深刻的理解,也为下一篇讲如何实现一个promise库作准备。(写完以后以为好水。。。)git

英文版本的规范见这里,segmentfault上也有人把规范翻译为中文,见这里github

在此,我主要是经过使用例子,讲解一下规范中then方法和Promise Resolution Procedure的每一种状况。web

constructor

规范中对于构造函数没有明确说明,因此在此处拿出来说解一下。segmentfault

和普通JavaScript对象同样,咱们一样是经过new关键词来建立一个Promise对象实例。构造函数只接收一个参数,且该参数必须是一个函数,任何其余的值好比undefinednull5true等都会报一个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有三种状态,pendingfulfilledrejected,实例刚建立时处于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是个什么东西呢?它是根据什么变为fulfilledrejected的呢?这就是咱们接下来要讲解的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 若是promisevalue指向同一个对象,则rejectpromise并以一个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来调用,则传入rreject promise,例:

new Promise((resolve)=>{
    resolve({then:(resolvePromise, rejectPromise)=>{
        rejectPromise(new Error('error'))
        }
    })
}).catch((reason)=>{
    console.log(reason)
})
// Error: error(…)

2.3.3.3.3 若是resolvePromiserejectPromise都被调用,或其中一个被调用了屡次,则以第一次调用的为准,并忽略以后的调用。例:

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 若是resolvePromiserejectPromise已经调用,则忽略它,例:

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 不然,则传入ereject 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不是一个函数,则传入valuefulfill 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 onFulfilledonRejected都是可选的
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以后调用,且把promisevalue做为它的第一个参数

这个从咱们全部的例子中均可以看出

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以后调用,且把promisereason做为它的第一个参数
2.2.3.2 它不能够在promise变为rejected以前调用
2.2.3.3 它只能够被调用一次

onRejectedonFulfilled基本相似,这里再也不次赘述

2.2.4 onFulfilledonRejected是在执行环境中仅包含平台代码时调用

这里有一个备注,平台代码是指引擎、执行环境、以及promise的实现代码。实际过程当中,要确保onFulfilledonRejected是异步执行的,它是在event loop过程当中then方法被调用以后的新调用栈中执行。咱们可使用setTimeoutsetImmediatemacro-task机制来实现,也可使用MutationObserverprocess.nextTickmicro-task机制来实现。promise的实现自己就被看做是平台代码,它自己就包含一个处理器能够调用的任务调度队列。

才疏学浅,没理解它这一条到底要表达一个什么意思。。。应该指的就是异步执行,由于异步执行的时候,页面中同步的逻辑都已经执行完毕,因此只剩下平台代码。

注:原生的Promise实现属于micro-task机制。macro-taskmicro-task分别是两种异步任务,它们的不一样后面会单独讲一下。下面列出了常见的异步方法都属于那种异步机制:

macro-task: script(总体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-task: process.nextTick, 原生Promise, Object.observe, MutationObserver

2.2.5 onFulfilledonRejected必须做为函数来调用,没有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 若是onFulfilledonRejected返回了一个值x,则执行resolve(promise2, x)

Promise.resolve(5).then(function(value){
    return ++value
}).then(function(value){
    console.log(value)
})
// 6

2.2.7.2 若是onFulfilledonRejected抛出了异常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库。

相关文章
相关标签/搜索