这是我参与新手入门的第二篇文章。
Promise 无论是在面试还是项目中都至关重要,所以是很有必要熟悉掌握promise的。
前言
阅读本文需要具备的知识:
- ES6
- Promise 基础知识
- js 事件机制
一丶什么是PromiseA+规范?
Promise 规范有很多,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+。ES6中则采用了 Promise/A+ 规范。
Promise/A+ 规范表现形式:
-
可以通过链式编程的方式对异步操作进行同步处理。
-
上一个操作的输出值是下一个操作的输入值。
ECMAScript 6 增加了对 Promises/A+规范的完善支持,即 Promise 类型。一经推出,Promise 就 大受欢迎,成为了主导性的异步编程机制。所有现代浏览器都支持 ES6 期约,很多其他浏览器 API(如 fetch()和 Battery Status API)也以期约为基础。—————《javaScript高级设计程序第四版》
二丶 Promise 的状态
拥有三种状态:
-
pending 待定
-
fulfilled 兑现
-
rejected 拒绝
-
promise对象初始化状态为pending -
当调用
resolve(成功),会由pending => fulfilled -
当调用
reject(失败),会由pending => rejected
注意一个Promise的当前状态必须为以上三种状态中的一种。 处于待定时可以移至执行状态或者拒绝状态。处于执行状态不能迁移至其他任何状态,必须拥有一个不可变的终值。处于拒绝态时,不能迁移至其他任何状态,必须拥有一个不可变的据因。
三丶先来看看原生promise的使用。
new Promise((resolve, reject) => {
resolve('fulfilled!')
}).then((res, err) => {
console.log(res);
})
输出结果 'fulfilled!'。
四丶实现我们的promise。
class MyPromise {
constructor(executor){
// 因为promise接收的是一个参数所以用户传入的不是函数抛出一个错误
if(typeof executor !== 'function'){
throw new TypeError('Promise resolver ${executor} is not a function')
}
const resolve = () => {
}
const reject = () => {
}
executor(resolve,reject)
}
}
以上代码就是我们的promise初始化了,我们先来跑一下~
new MyPromise((reslove, reject) => {
console.log('my promise');
})
// 执行结果: my promise。
我们再来传入一个不合法的值来测试下。
let test = 'my promise'
new MyPromise(test)
// 执行结果:Uncaught TypeError: Promise resolver ${executor} is not a function
我们成功的模仿出了promise的第一步了,错误机制。继续完善我们的promise。
class MyPromise {
constructor(executor){
// 因为promise接收的是一个参数所以用户传入的不是函数抛出一个错误
if(typeof executor !== 'function'){
throw new TypeError('Promise resolver ${executor} is not a function')
}
this.initValue()
executor(this.resolve,this.reject)
}
initValue(){
//记录状态和值的改变
this.value = null //终值
this.reason = null //拒因
this.state = 'pending' //状态
}
resolve(value){
//成功后的一系列操作(状态的改变,成功回调的执行)
if(this.state === 'pending'){
// 状态进行改变
this.state = 'fulfilled'
// 执行成功的回调,把终值进行赋值
this.value = value
}
}
reject(reason){
//失败后的一系列操作(状态的改变,失败回调的执行)
if(this.state === 'pending'){
// 状态进行改变
this.state = 'rejected'
// 失败后的回调,把据因进行赋值
this.reason = reason
}
}
}
测试一下。
new MyPromise((reslove, reject) => {
console.log('my promise');
// 执行结果 mypromise
})
实现 then 方法。
class MyPromise {
constructor(executor) {
// 因为promise接收的是一个参数所以用户传入的不是函数抛出一个错误
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver ${executor} is not a function')
}
this.initValue()
executor(this.resolve, this.reject)
}
initValue() {
//记录状态和值的改变
this.value = null //终值
this.reason = null //拒因
this.state = MyPromise.PENDING //状态
}
resolve(value) {
//成功后的一系列操作(状态的改变,成功回调的执行)
if (this.state === 'pending') {
// 状态进行改变
this.state = 'fulfilled'
// 执行成功的回调,把终值进行赋值
this.value = value
}
}
reject(reason) {
//失败后的一系列操作(状态的改变,失败回调的执行)
if (this.state === MyPromise.PENDING) {
// 状态进行改变
this.state = MyPromise.REJECTED
// 失败后的回调,把据因进行赋值
this.reason = reason
}
}
then(fulfilled, rejected) {
if (typeof fulfilled !== 'function') {
fulfilled = (value) => {
return value
}
}
if (typeof rejected !== 'function') {
rejected = (reason) => {
throw reason
}
}
if (this.state === MyPromise.FULFILLED) {
fulfilled(this.value)
}
if (this.state === MyPromise.REJECTED) {
rejected(this.reason)
}
}
}
MyPromise.PENDING = 'pending'
MyPromise.FULFILLED = 'fulfilled'
MyPromise.REJECTED = 'reject'}
}
测试下
new MyPromise((resolve, reject) => {
console.log('my promise')
resolve('test then')
}).then(res => {
console.log('res', res)
}, res => {
console.log('res', res)
})
// 执行结果 Cannot read property 'state' of undefined
现报错了,我们找下原因,说找不到 state 。 这是因为this 在运行期间已经不指向当前实例了,所以就没有这个属性,自然就报错了。我们再来完善下,使用bind 解决this指向问题。使用bind 绑定的this 后面的指向不会在该变了。
class MyPromise {
constructor(executor) {
// 因为promise接收的是一个参数所以用户传入的不是函数抛出一个错误
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver ${executor} is not a function')
}
this.initValue()
this.initBind() // 绑定this
executor(this.resolve, this.reject)
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
initValue() {
//记录状态和值的改变
this.value = null //终值
this.reason = null //拒因
this.state = MyPromise.PENDING //状态
}
resolve(value) {
//成功后的一系列操作(状态的改变,成功回调的执行)
if (this.state === 'pending') {
// 状态进行改变
this.state = 'fulfilled'
// 执行成功的回调,把终值进行赋值
this.value = value
}
}
reject(reason) {
//失败后的一系列操作(状态的改变,失败回调的执行)
if (this.state === MyPromise.PENDING) {
// 状态进行改变
this.state = MyPromise.REJECTED
// 失败后的回调,把据因进行赋值
this.reason = reason
}
}
then(fulfilled, rejected) {
if (typeof fulfilled !== 'function') {
fulfilled = (value) => {
return value
}
}
if (typeof rejected !== 'function') {
rejected = (reason) => {
throw reason
}
}
if (this.state === MyPromise.FULFILLED) {
fulfilled(this.value)
}
if (this.state === MyPromise.REJECTED) {
rejected(this.reason)
}
}
}
MyPromise.PENDING = 'pending'
MyPromise.FULFILLED = 'fulfilled'
MyPromise.REJECTED = 'reject'
再次测试。
new MyPromise((resolve, reject) => {
console.log('my promise')
resolve('test then')
}).then(res => {
console.log('res', res)
}, res => {
console.log('res', res)
})
/* 执行结果
* my promise
* res test then
*/
五丶异步实现。
到这一步我们已经实现了基本的promise 了。现在我们看一下一个原生的promise 的这段代码执行顺序。
console.log('1')
new Promise((resolve, reject) => {
console.log('2')
resolve('测试promise')
}).then(res => {
console.log('4')
console.log('res', res)
}, res => {
console.log('res', res)
})
console.log('3')
/*
* 执行结果
* 1
* 2
* 3
* 4
* res 测试promise
*/
再看下我们的。
console.log('1')
new MyPromise((resolve, reject) => {
console.log('2')
resolve('测试promise')
}).then(res => {
console.log('4')
console.log('res', res)
}, res => {
console.log('res', res)
})
console.log('3')
/*
* 执行结果
* 1
* 2
* 4
* res 测试promise
* 3
*/
原因是因为原生promise 是异步的,我们并没有去做异步的处理,我们用setTimeout 去做异步的处理。
class MyPromise {
constructor(executor) {
// 因为promise接收的是一个参数所以用户传入的不是函数抛出一个错误
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver ${executor} is not a function')
}
this.initValue()
this.initBind() // 绑定this
executor(this.resolve, this.reject)
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
initValue() {
//记录状态和值的改变
this.value = null //终值
this.reason = null //拒因
this.state = MyPromise.PENDING //状态
}
resolve(value) {
//成功后的一系列操作(状态的改变,成功回调的执行)
if (this.state === 'pending') {
// 状态进行改变
this.state = 'fulfilled'
// 执行成功的回调,把终值进行赋值
this.value = value
}
}
reject(reason) {
//失败后的一系列操作(状态的改变,失败回调的执行)
if (this.state === MyPromise.PENDING) {
// 状态进行改变
this.state = MyPromise.REJECTED
// 失败后的回调,把据因进行赋值
this.reason = reason
}
}
then(fulfilled, rejected) {
if (typeof fulfilled !== 'function') {
fulfilled = (value) => {
return value
}
}
if (typeof rejected !== 'function') {
rejected = (reason) => {
throw reason
}
}
if (this.state === MyPromise.FULFILLED) {
setTimeout(() => {
fulfilled(this.value)
})
}
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
rejected(this.reason)
})
}
}
}
MyPromise.PENDING = 'pending'
MyPromise.FULFILLED = 'fulfilled'
MyPromise.REJECTED = 'reject'
加上了setTimeout 的 then 方法我们在次测试下。
console.log('1')
new MyPromise((resolve, reject) => {
console.log('2')
resolve('测试promise')
}).then(res => {
console.log('4')
console.log('res', res)
}, res => {
console.log('res', res)
})
console.log('3')
/*
* 执行结果
* 1
* 2
* 3
* 4
* res 测试promise
*/
这下是不是和原生输出的一样啦。在 MyPromis 里放入一个异步操作会怎么样呢?
console.log('1')
new MyPromise((resolve, reject) => {
setTimeout(() => {
console.log('2')
resolve('测试promise')
})
}).then(res => {
console.log('4')
console.log('res', res)
}, res => {
console.log('res', res)
})
console.log('3')
发现 4 没有输出。这是因为刚开始 setTimeout 并没有执行,由于js的事件机制把他放到事件列队里了。接下来开始执行 then 方法。可是这时 MyPromise 的状态还是 pending,then 里面的两个判断为 fulfilled 和 reject 状态的方法都没有执行。所有 then 没有执行,最后的结果就是 1 3 2 了。
// 没有执行
if (this.state === MyPromise.FULFILLED) {
setTimeout(() => {
fulfilled(this.value)
})
}
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
rejected(this.reason)
})
}
在我们的MyPromise 里新增待定状态。
class MyPromise {
constructor(executor) {
// 因为promise接收的是一个参数所以用户传入的不是函数抛出一个错误
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver ${executor} is not a function')
}
this.initValue()
this.initBind() // 绑定this
executor(this.resolve, this.reject)
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
initValue() {
//记录状态和值的改变
this.value = null //终值
this.reason = null //拒因
this.state = MyPromise.PENDING //状态
this.onFulfilledCallbacks = [] //成功回调
this.onRejectedCallbacks = [] //失败回调
}
resolve(value) {
//成功后的一系列操作(状态的改变,成功回调的执行)
if (this.state === 'pending') {
// 状态进行改变
this.state = 'fulfilled'
// 执行成功的回调,把终值进行赋值
this.value = value
//成功以后执行
this.onFulfilledCallbacks.forEach((fn) => fn(this.value)
)
}
}
reject(reason) {
//失败后的一系列操作(状态的改变,失败回调的执行)
if (this.state === MyPromise.PENDING) {
// 状态进行改变
this.state = MyPromise.REJECTED
// 失败后的回调,把据因进行赋值
this.reason = reason
// 失败以后执行
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(fulfilled, rejected) {
if (typeof fulfilled !== 'function') {
fulfilled = (value) => {
return value
}
}
if (typeof rejected !== 'function') {
rejected = (reason) => {
throw reason
}
}
if (this.state === MyPromise.FULFILLED) {
setTimeout(() => {
fulfilled(this.value)
})
}
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
rejected(this.reason)
})
}
// 增加待定状态
if (this.state === MyPromise.PENDING) {
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
rejected(this.reason)
})
})
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
fulfilled(value)
})
})
}
}
}
MyPromise.PENDING = 'pending'
MyPromise.FULFILLED = 'fulfilled'
MyPromise.REJECTED = 'reject'
测试下
console.log('1')
new MyPromise((resolve, reject) => {
setTimeout(() => {
console.log('2')
resolve('测试promise')
})
}).then(res => {
console.log('4')
console.log('res', res)
}, res => {
console.log('res', res)
})
console.log('3')
/*
* 执行结果
* 1
* 2
* 3
* 4
* res 测试promise
*/
我们在 then 方法里又判断了 pending 状态,把兑现和拒绝的回调都保存起来。当在执行 resolve 或者 reject 时会把状态为 pending 时存起来的方法执行一遍。
六丶链式调用
先看下原生的链式调用。
new Promise((resolve, reject) => {
resolve('测试链式调用')
})
.then(
value => {
return '终值' + value
},
reason => {
console.log('拒因', reason)
}
)
.then(
value => {
console.log('终值', value)
},
reason => {
console.log('拒因', reason)
}
)
// 执行结果:终值 终值测试链式调用
实现链式调用
class MyPromise {
constructor(executor) {
// 因为promise接收的是一个参数所以用户传入的不是函数抛出一个错误
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver ${executor} is not a function')
}
this.initValue()
this.initBind() // 绑定this
executor(this.resolve, this.reject)
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
initValue() {
//记录状态和值的改变
this.value = null //终值
this.reason = null //拒因
this.state = MyPromise.PENDING //状态
this.onFulfilledCallbacks = [] //成功回调
this.onRejectedCallbacks = [] //失败回调
}
resolve(value) {
//成功后的一系列操作(状态的改变,成功回调的执行)
if (this.state === 'pending') {
// 状态进行改变
this.state = 'fulfilled'
// 执行成功的回调,把终值进行赋值
this.value = value
//成功以后执行
this.onFulfilledCallbacks.forEach((fn) => fn(this.value)
)
}
}
reject(reason) {
//失败后的一系列操作(状态的改变,失败回调的执行)
if (this.state === MyPromise.PENDING) {
// 状态进行改变
this.state = MyPromise.REJECTED
// 失败后的回调,把据因进行赋值
this.reason = reason
// 失败以后执行
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(fulfilled, rejected) {
if (typeof fulfilled !== 'function') {
fulfilled = (value) => {
return value
}
}
if (typeof rejected !== 'function') {
rejected = (reason) => {
throw reason
}
}
// 实现链式调用,且改变了后面的then的值,必须通过新的实例
let promise_ = new MyPromise((resolve, reject) => {
if (this.state === MyPromise.FULFILLED) {
setTimeout(() => {
try {
const x = fulfilled(this.value)
resolve(x)
} catch (e) {
reject(e)
}
})
}
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
try {
const x = rejected(this.reason)
resolve(x)
} catch (e) {
reject(e)
}
})
}
//在promise.js里面肯定要追加一个状态的判断
if (this.state === MyPromise.PENDING) {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
try {
const x = fulfilled(value)
resolve(x)
} catch (e) {
reject(e)
}
})
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
const x = rejected(this.reason)
resolve(x)
} catch (e) {
reject(e)
}
})
})
}
})
return promise_
}
}
MyPromise.PENDING = 'pending'
MyPromise.FULFILLED = 'fulfilled'
MyPromise.REJECTED = 'reject'
这里不是很好理解,我们来逐步分析。首先看一下一个 then 的时候 MyPromise 的内部都发生了什么。
new MyPromise((resolve, reject) => {
resolve('测试链式调用')
})
.then(
value => {
return '终值1' + value
},
reason => {
console.log('拒因', reason)
}
)
- 先执行初始化步骤。
- 执行
resolve。此时的resolve只是把外部的resolve值 保存到Mypromise里的终值属性上了,并把状态改为fulfilled。 - 执行
then方法,此时的MyPromise的状态为`fulfilled。
如图可以看出有个resolve 方法 ,这是实现链式调用的关键,x 是终值, 因为此时的状态还是fulfilled 所以 fulfilled 方法并没有通过判断执行,如果没有新的链式调用这会是最后一个 then。
测试多个then。
new MyPromise((resolve, reject) => {
resolve('测试链式调用')
})
.then(
value => {
console.log(value);
return '终值1' + value
},
reason => {
console.log('拒因', reason)
}
)
.then(
value => {
console.log('终值', value)
},
reason => {
console.log('拒因', reason)
}
)
此时链式调用上又多了一个 then 方法,继续上面第三步,此时有了新的 then 方法。MyPrmise 在内部 then 方法中由于又被初始化了。状态也变为了 pending,接下来就是根据上面的步骤以此类推了。
当 MyPromise 返回的值不是一个普通的值会发生什么?
new MyPromise((resolve, reject) => {
resolve(1)
})
.then(
value => {
return new MyPromise((resolve) => {
resolve('终值')
})
},
reason => {
console.log('reason', reason)
}
)
.then(
value => {
console.log('value', value)
},
reason => {
console.log('reason', reason)
}
)
/*
* onFulfilledCallbacks: []
* onRejectedCallbacks: []
* reason: null
* reject: ƒ ()
* resolve: ƒ ()
* state: "fulfilled"
* value: "终值"
*/
当 MyPromise 的值不是一个普通的值, 而是promise实例得时候,必须等待这一个promise的时候结束,才能进行进一步执行。
所以规范提出了一个解决方案 针对resolvePromise的具体解决过程
class MyPromise {
constructor(executor) {
// 因为promise接收的是一个参数所以用户传入的不是函数抛出一个错误
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver ${executor} is not a function')
}
this.initValue()
this.initBind() // 绑定this
executor(this.resolve, this.reject)
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
initValue() {
console.log(666);
//记录状态和值的改变
this.value = null //终值
this.reason = null //拒因
this.state = MyPromise.PENDING //状态
this.onFulfilledCallbacks = [] //成功回调
this.onRejectedCallbacks = [] //失败回调
}
resolve(value) {
//成功后的一系列操作(状态的改变,成功回调的执行)
if (this.state === MyPromise.PENDING) {
this.state = MyPromise.FULFILLED
// 执行成功的回调,把终值进行赋值
this.value = value
//成功以后执行
this.onFulfilledCallbacks.forEach((fn) => {
fn(this.value)
})
}
}
reject(reason) {
//失败后的一系列操作(状态的改变,失败回调的执行)
if (this.state === MyPromise.PENDING) {
// 状态进行改变
this.state = MyPromise.REJECTED
// 失败后的回调,把据因进行赋值
this.reason = reason
// 失败以后执行
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
then(fulfilled, rejected) {
if (typeof fulfilled !== 'function') {
fulfilled = (value) => {
return value
}
}
if (typeof rejected !== 'function') {
rejected = (reason) => {
throw reason
}
}
// console.log(fulfilled);
// 实现链式调用,且改变了后面的then的值,必须通过新的实例
let promise_ = new MyPromise((resolve, reject) => {
console.log('fulied'); // FULFILLED
if (this.state === MyPromise.FULFILLED) {
console.log(777);
setTimeout(() => {
try {
const x = fulfilled(this.value) // 状态因为是fulfilled所以用fulfilled函数
console.log(promise_);
MyPromise.resolvePromise(promise_, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
try {
const x = rejected(this.reason)
MyPromise.resolvePromise(promise_, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
//在promise.js里面肯定要追加一个状态的判断
if (this.state === MyPromise.PENDING) {
console.log('pending');
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
try {
const x = fulfilled(value)
MyPromise.resolvePromise(promise_, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
const x = rejected(this.reason)
MyPromise.resolvePromise(promise_, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise_
}
}
MyPromise.PENDING = 'pending'
MyPromise.FULFILLED = 'fulfilled'
MyPromise.REJECTED = 'reject'
MyPromise.resolvePromise = function (promise_, x, resolve, reject) {
// x 与 promise 相等
if (promise_ === x) {
reject(new TypeError('Chaining cycle detected for promise'))
}
let called = false
if (x instanceof MyPromise) {
// 递归判断 x 为 Promise
x.then(
value => {
MyPromise.resolvePromise(promise_, value, resolve, reject)
},
reason => {
reject(reason)
}
)
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// x 为对象或函数
try {
const then = x.then
if (typeof then === 'function') {
then.call(
x,
value => {
if (called) return
called = true
MyPromise.resolvePromise(promise_, value, resolve, reject)
},
reason => {
if (called) return
called = true
reject(reason)
}
)
} else {
if (called) return
called = true
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
测试一下。
new MyPromise((resolve, reject) => {
resolve(1)
})
.then(
value => {
return new MyPromise((resolve) => {
resolve('终值')
})
},
reason => {
console.log('reason', reason)
}
)
.then(
value => {
console.log('value', value)
},
reason => {
console.log('reason', reason)
}
)
// 输出结果: value 终值
测试PromiseA+
我们需要安装一个插件来进行测试我们的 promise 是否符合规范。
npm install promises-aplus-tests
然后将这端代码复制到我们写的 promise 里。
MyPromise.defer = MyPromise.deferred = function () {
let dfd = {}
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = MyPromise;
最后输入指令测试。
npx promises-aplus-tests index.js
通过测试恭喜你又掌握了一项技能!
后续更新关于promise.all() , promise.race() 等方法。
总结
一个属于我们的promise就做好啦!
源码地址: git@github.com:cn0379/Promise-Aplus.git