Promise引见--范例篇

本篇文章是Promise系列文章的第二篇,主假如解说基于Promise/A+范例,在传入差别范例的参数时,promise内部离别会怎样处置惩罚。本章的重要目标是让人人对promise有一个越发深切的明白,也为下一篇讲怎样完成一个promise库做准备。(写完以后觉得好水。。。)

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

在此,我主假如经由过程运用例子,解说一下范例中then要领和Promise Resolution Procedure的每一种状况。

constructor

范例中关于组织函数没有明白申明,所以在此处拿出来解说一下。

和一般JavaScript对象一样,我们一样是经由过程new关键词来建立一个Promise对象实例。组织函数只吸收一个参数,且该参数必需是一个函数,任何其他的值比方undefinednull5true等都会报一个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有三种状况,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。以下:

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库。

    原文作者:武官尚书
    原文地址: https://segmentfault.com/a/1190000007719379
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞