知识概述
- 所谓回调函数,就是先传入一个函数,在合适的时机里调用这个函数。
- 规范:https://promisesaplus.com/#notes
- 要实现Promise, 就应该按照它的规范来。
一个简单的没有链式调用的Promise
要点
- Promise States,3种状态,从
pending
到fulfilled
, 或 从pending
到rejected
- then方法
const PENDING = 'pending'
const REJECTED = 'rejected'
const FULFILLED = 'fulfilled'
function _resolve(val) {
this.status = FULFILLED
this.value = val
this.onFulfilledList.forEach(fn => fn(this.value))
this.onFulfilledList= []
}
function _reject(val) {
this.status = REJECTED
this.reason = val
this.onRejectedList.forEach(fn => fn(this.reason))
this.onRejectedList = []
}
class CustomePromise {
constructor (executor) {
this.value = undefined
this.reason = undefined
this.status = PENDING
this.onFulfilledList = []
this.onRejectedList = []
executor(_resolve.bind(this), _reject.bind(this))
}
/**
* 在then里面如果状态为pending,则收集回调,放在一个数组里,因为then可能执行多次;否则立即执行
* 在resolve或者reject中执行这些回调
* 说明回调的执行在2个地方,then里面:当状态不为pending; resolve/reject里面,执行缓存的数组回调
*/
then(onFulfilled, onRejected) {
if (this.status === PENDING) {
typeof onFulfilled === 'function' && this.onFulfilledList.push(onFulfilled)
typeof onRejected === 'function' && this.onRejectedList.push(onRejected)
}
if (this.status === FULFILLED) {
typeof onFulfilled === 'function' && onFulfilled(this.value)
}
if (this.status === REJECTED) {
typeof onRejected === 'function' && onRejected(this.reason)
}
}
catch () {
}
finally() {
}
static resolve() {
}
static reject() {
}
static all() {
}
static race() {
}
static allSettled() {
}
static any() {
}
}
// 测试数据
const p = new CustomePromise((resolve, reject) => {
// setTimeout(reject, 1000, 'test')
reject('testets')
})
p.then(null, console.log)
p.then(console.log,(str) => {
console.log(str + 1111)
})
链式调用
** 要点**
- then 方法必须返回一个 promise 对象,设为promise2, 原来的promise对象设为promise1
- promise1的返回值会影响到promise2的状态,以此类推,这涉及到Promise 解决过程
Promise 解决过程是一个抽象的操作,其需输入一个
promise
和一个值,我们表示为[[Resolve]](promise, x)
,如果x
有then
方法且看上去像一个 Promise ,解决程序即尝试使promise
接受x
的状态;否则其用x
的值来执行promise
。
这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的then
方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。
运行 [[Resolve]](promise, x)
需遵循的步骤参考规范:https://www.ituring.com.cn/article/66566
说人话就是我们怎么去处理一个promise的返回值,因为这个返回值会影响我们生成的新的promise的状态。我们根据Promise解决过程的规范来实现。
const PENDING = 'pending'
const REJECTED = 'rejected'
const FULFILLED = 'fulfilled'
function _resolve (val) {
this.status = FULFILLED
this.value = val
this.onFulfilledList.forEach((fn) => fn(this.value))
}
function _reject (val) {
this.status = REJECTED
this.reason = val
this.onRejectedList.forEach((fn) => fn(this.reason))
}
function isFn (param) {
return typeof param === 'function'
}
// promise解决过程
function resolvePromise (promise2, x, resolve, reject) {
if(promise2 === x){
reject(new TypeError('Chaining cycle'))
}
if(x && typeof x === 'object' || typeof x === 'function'){
let used;
try {
let then = x.then
if(typeof then === 'function'){
then.call(x, (y)=>{
if (used) return;
used = true
resolvePromise(promise2, y, resolve, reject)
}, (r) =>{
if (used) return;
used = true
reject(r)
})
} else {
if (used) return;
used = true
resolve(x)
}
} catch(e){
if (used) return;
used = true
reject(e)
}
} else {
resolve(x)
}
}
class CustomePromise {
constructor(executor) {
this.value = undefined
this.reason = undefined
this.status = PENDING
this.onFulfilledList = []
this.onRejectedList = []
executor(_resolve.bind(this), _reject.bind(this))
}
/**
* 在then里面如果状态为pending,则收集回调,放在一个数组里,因为then可能执行多次;否则立即执行
* 在resolve或者reject中执行这些回调
* 说明回调的执行在2个地方,then里面:当状态不为pending; resolve/reject里面,执行缓存的数组回调
*/
then (onFulfilled, onRejected) {
/**
* 这一步完善简单的promise,因为要处理返回值,并且对这个返回值进行处理,把处理过程封装在resolvePromise里
* 由于then的回调是异步执行的,因此我们需要把onFulfilled和onRejected执行放到异步中去执行,同时做一下错误的处理:
*/
let _this = this
onFulfilled = isFn(onFulfilled) ? onFulfilled : (value) => value
onRejected = isFn(onRejected) ? onRejected : (reason) => { throw reason }
const promise2 = new Promise((resolve, reject) => {
if (this.status === PENDING) {
_this.onFulfilledList.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(_this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
_this.onRejectedList.push(() => {
setTimeout(() => {
try {
let x = onRejected(_this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(_this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(_this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
})
return promise2
}
catch () { }
finally () { }
static resolve () { }
static reject () { }
static all () { }
static race () { }
static allSettled () { }
static any () { }
}
// 测试数据
// const p = new CustomePromise((resolve, reject) => {
// // setTimeout(reject, 1000, 'test')
// reject('testets')
// })
// p.then(null, console.log)
// p.then(console.log,(str) => {
// console.log(str + 1111)
// })
// 测试promise规范,注意要先安装promises-aplus-tests
CustomePromise.defer = CustomePromise.deferred = function () {
let dfd = {}
dfd.promise = new CustomePromise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = CustomePromise
测试
- 安装 promises-aplus-tests
- 在定义Promise后面加入下面的代码
CustomePromise.defer = CustomePromise.deferred = function () {
let dfd = {}
dfd.promise = new CustomePromise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = CustomePromise
CustomePromise是我自定义的,这里填写你们自己的
-
package.json
添加命令
"scripts": {
"testPromise": "promises-aplus-tests src/promise/CustomePromise.js"
},
这里添加你们自己的文件路径
- 运行
yarn testPromise
, 测试结果如下