排版很不友好,抱歉
/*
* 实现Promise是根据Promise规范来的:https://promisesaplus.com/
* 规范很短,所以每句都很重要
* Promise特点
* 1. 状态改变后不可再改变(状态凝固)
* pending,fullfilled,rejected
*
* 2. then方法(此方法就是最重要的部分)
* 可以链式调用
* then方法返回一个新的promise实例
* promise2 = promise.then(f1, f2)
* 当promise的状态确定的时候会执行then中的f1或者f2。
* 在f1/f2执行之前then就已经返回了一个Promise实例(promise2)
* promise2的状态由f1/f2的返回值确定,以此类推就是链式调用
*
* 3. promise创建时传入的函数会立即执行
*/
/*
* 在考虑某些函数执行时同步还是异步,考虑四种情况,分别会有什么问题
* 假设new一个Promise时传入的函数为funA, then中的函数为funCD
* 1 funA是同步,funCD为同步
* 2 funA是同步,funCD为异步 // 为什么最后只能用这种方式
* 3 funA是异步,funCD为同步
* 4 funA是异步,funCD为异步
*/
/*
* 问题: 1,状态是怎么改变的(这是个好问题,是面试官提的。当时我真的没答出来)
* 在执行resolve,reject函数时会改变状态。看resolve,reject的函数声明
* 2,new Promise时传入的函数为什么要立即执行,then里传的两个函数为什么要异步执行?
* resolve,reject函数是用户决定什么时候执行的,同步、异步执行都是用户的行为
* 这里可以好好分析下,信息量很大
*/
/*
* 状态确定后挨个调用数组中的方法
* 状态是在resolve,reject函数执行时改变的
*/
function Promise(executor) {
var self = this
/*
* 为什么要创建数组?
* new出第一个实例promise后,可以调用多次then
* 而此时promise的状态可能还不确定,所以来个数组保存下then中的函数,等到状态确定后再执行then中的函数。
* 保存then中的回调函数,注意查看什么时候push进去的
*/
self.resolvedCallbacks = []
self.rejectedCallbacks = []
// 状态
self.status = 'pending'
/*
* resolve中应该包含一个对象,因为每次创建一个实例
* 彼此间都不会有影响(这难道不是单例模式么?还是工厂函数?)
* 状态[确定]后挨个调用数组中的方法,必须被当做函数调用。
* 即调用时函数体内的this应该是undefined/window
* --------------------------------------------------
* 成功传值,失败传原因
* --------------------------------------------------
* resolve和reject是什么时候被执行的呢?
* 这两个函数是在executor体内被执行的
* --------------------------------------------------
* 对于在resolve,reject两个函数中
* 异步执行resolvedCallbacks,rejectedCallbacks中函数问题
*
*/
function resolve(value) {
// 测试用例给我们传了一个我自己定义的Promise实例时
if (value instanceof Promise) {
value.then(resolve, reject)
return
}
// 这里为什么也要异步
// 因为数组resolvedCallbacks,rejectedCallbacks不能跟resolve同步执行。为什么?
setTimeout(function() {
/*
* 这里是为了确保状态不可改变,即resolve/reject只执行一次
*/
if (self.status === 'pending') {
self.status = 'resolved'
// 保存传入的参数,promise成功时的值
self.data = value
var f
// 性能优先,使用原始for循环
for (var i = 0; i < self.resolvedCallbacks.length; i++) {
/*
* 为什么分开写?
* 确保是函数的调用,即被调用时函数体内的this为undefined/window
* 标准里都有说明
*/
f = self.resolvedCallbacks[i]
f(value)
}
}
})
}
function reject(reason) {
// 为什么是异步
setTimeout(function() {
if (self.status === 'pending') {
self.status = 'rejected'
self.data = reason
var f
for (var i = 0; i < self.rejectedCallbacks.length; i++) {
f = self.rejectedCallbacks[i]
f(reason)
}
}
})
}
try {
// promise创建时传入的函数会立即执行,而传入的函数不知道有没有问题.所以要try下
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
/*
* then方法中实现根据状态调用不同的函数,
* 并且在调用回调前返回一个新的promise且此promise的状态由回调函数确定
* pending -> fulfilled : 调用onResolved
* pending -> rejected : 调用onRejected
* onRejected\onResolved必须是函数,否则忽略它
*/
Promise.prototype.then = function(onResolved, onRejected) {
var self = this
if (typeof onResolved !== 'function') {
/*
* 为什么要这样?
* 例如这种情况:p2 = p1.then(null, f2),没有传onResolved函数
* p1成功了,应该调用onResolved函数。
* 正常情况下p2的状态由onResolved确定,而onResolved没有东西。
* 此时p2的状态应该由p1决定。
* 怎么拿到p1的状态?在then中成功时调用的函数将p1的状态返回就拿到p1的状态!
* 在没有返回函数情况下,将onResolved用一个函数代替
* ---------------------------------------------------------------
* 为什么可以这么写,我没有吃透
*
* 这个函数被执行的时候value就有值了,状态改变onResolved(self.data)执行
* 在这种情况下value的值查查self.data
* self.data是在构造函数里的resolve(value)里被赋值的。所以这里非常的绕,还需要再来理解消化
*
*/
onResolved = function(value) { return value }
}
if (typeof onRejected !== 'function') {
/*
* 为什么要这样?
* 例如这种情况:p2 = p1.then(null, f2),没有传onResolved函数
* p1失败,p2也应该失败。
* 这里throw,后面的try可以catch到。catch到就会reject(e)
* 将p1的失败reason抛出来,p2也就失败了
* 具体的过程跟上面的函数是一样的
*/
onRejected = function(reason) { throw reason}
}
/*
* 实例在三种状态下都可以调用then方法
* 判断状态的三种情况。为了方便
* 在三种情况下
* 我要实现的是
*/
var promise2
if (self.status === 'resolved') {
/*
* 返回一个新的promise2,在promise2中调用onResolved且传入promise成功时传入的值
* 而promise成功时传入的值到底是什么,我们是不知道的。
* 这是promise中最复杂的!!!
* ----------------------------------------------------------------------
* onResolved返回值x决定了promise2的状态
*
*/
promise2 = new Promise(function(resolve, reject) {
// 为什么这里要异步?TODO
setTimeout(function() {
try {
/*
* 这个data是第一个promise中传入的函数中的resolve携带的参数!!!!
* onResolved是在then里的,
* 如果onResolved返回的又是Promise实例x呢,
* --------------------------------------------------------------
* 这次的promise的状态是由返回的新的Promise实例决定
* 而新的实例的状态什么时候确定呢?我们不用管,只需要在其后挂个then。
* then中函数运行的时候就是状态确定的时候
* --------------------------------------------------------------
* 直接resolve是不行的,需要在返回的实例x中的then里resolve值
*/
var x = onResolved(self.data)
// 非兼容
// if (x instanceof Promise) {
// // 非简化。可以想想了,又是递归?
// // x.then(function(value) {
// // resolve(value)
// // }, function(reason) {
// // reject(reason)
// // })
// // 简化
/*
* 这种简化让人头疼,resolve,reject是需要传参的。问题来了,两个函数的参数是怎么传进去的
* 这里的resolve,reject其实就是onResolved和onRejected。这种情况又是递归了
*/
// x.then(resolve, reject)
// } else {
// resolve(x)
// }
// 兼容下
RESOLVE_PROMISE(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function(resolve, reject) {
// 为什么这里要异步?TODO
setTimeout(function() {
try {
var x = onRejected(self.data)
// 非兼容,如果x是Bluebird,Q里的promise呢?if就不会执行了
// if (x instanceof Promise) {
// x.then(resolve, reject)
// } else {
// // 为什么是resolve?
// resolve(x)
// }
// 兼容
RESOLVE_PROMISE(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
})
}
if (self.status === 'pending') {
/*
* promise1还是不确定状态,需要等到promise1确地了状态才能确定promise2应该怎么走
* 查看构造函数里resolve函数的注释,找找灵感。
* -------------------------------------------------------
* 为什么在这就不需要像上面的‘resolved’,‘rejected’里异步呢?
* 因为这里肯定是在将来才执行的
*/
promise2 = new Promise(function(resolve, reject) {
self.resolvedCallbacks.push(function(value) {
try {
var x = onResolved(self.data)
// 非兼容
// if (x instanceof Promise) {
// x.then(resolve, reject)
// } else {
// resolve(x)
// }
// 兼容
RESOLVE_PROMISE(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
self.rejectedCallbacks.push(function(reason) {
try {
var x = onRejected(self.data)
// 非兼容
// if (x instanceof Promise) {
// x.then(resolve, reject)
// } else {
// resolve(x)
// }
// 兼容
RESOLVE_PROMISE(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
})
})
}
return promise2
}
/*
* 上面就基本已经实现了Promise,但是没有达到 https://promisesaplus.com/ 规范的要求。
* 这个函数是多种实现中的一种
* 这个函数的作用是使其他的Promise可以互相兼容------兼容
* ------------------------------------------------------------------------
* 各参数说明
* promise:
* x:
* resolve:
* reject:
*/
function RESOLVE_PROMISE(promise, x, resolve, reject) {
if (promise === x) {
// 一个promise实例resolve了它自己
reject(new TypeError('Chaining cycle is deteced for promise'))
return
}
// 如果x是自己的Promise的实现
if (x instanceof Promise) {
if (x.status === 'pending') {
x.then(function(v) {
// god,又是递归
RESOLVE_PROMISE(promise, v, resolve, reject)
}, reject)
} else {
x.then(resolve, reject)
}
return
}
// 只有对象和函数这两种数据类型可以保存属性,然而typeof null也是'object'
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
/*
* 为什么要把then单独拿出来?避免副作用
* 因为我们是在读别人给的属性,但是我们不知道该属性具体是什么。也可能报错
* 如果该属性是getter,则每次调用就会返回不同的值。
* 所以只调用一次
* ---------------------------------------------------------------------
* a,b,c三者只能调用一个,为什么?TODO
* 避免原型链上有then函数(thenable)但却不是Promise的实现,但是为什么这样就可以避免,我并没有吃透
* ---------------------------------------------------------------------
* if语句里有两个可以可疑的函数,有函数名resolvePromise,rejectPromise。
* 既然是当参数传,我觉得不需要函数名应该也一样吧
*/
var then = x.then
var called = false // 记录执行状态
if (typeof then === 'function') {
then.call(x, function resolvePromise(y) {
if (called) {
return
}
called = true
// 产生了递归
RESOLVE_PROMISE(promise, y, resolve, reject)//--- a
}, function rejectPromise(r) {
if (called) {
return
}
called = true
reject(r) //------------------------------------- b
})
} else {
resolve(x)
}
} catch(e) {
if (called) {
return
}
called =true
reject(e) //----------------------------------------- c
}
} else {
resolve(x)
}
}