一、简易版Promise
// 三种状态是固定的常量
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function MyPromise(fn) {
const that = this
that.state = PENDING
// value初始值为undefined
that.value = undefined
that.resolvedCallbacks = []
that.rejectedCallbacks = []
// 待完善resolve和reject函数
// 待完善执行 fn 函数
}
- 首先我们创建了三个常量用于表示状态,对于经常使用的一些值都应该通过常量来管理,便于开发及后期维护。
- 在函数体内部首先创建了常量
that
,因为代码可能会异步执行,用于获取正确的this
对象 - 一开始
Promise
的状态应该是pending
value
变量用于保存resolve
或者reject
中传入的值resolvedCallbacks
和rejectedCallbacks
用于保存then
中的回调,因为当执行完Promise
时状态可能还是等待中,这时候应该把then
中的回调保存起来用于状态改变时使用
接下来我们来完善 resolve
和 reject
函数,添加在 MyPromise
函数体内部
function resolve(value) {
if (that.state === PENDING) {
that.state = RESOLVED
// 触发resolve方法时,value会被重新赋值
that.value = value
that.resolvedCallbacks.map(cb => cb(that.value))
}
}
function reject(value) {
if (that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map(cb => cb(that.value))
}
}
- 首先两个函数都得判断当前状态是否都为等待中,因为规范只有等待态才可以改变状态
- 将当前状态更改为对应状态,并且将闯入的值赋值给
value
- 遍历回调数组并执行
完成以上两个函数以后,我们就该实现如何执行Promise
中传入的函数了
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
- 实现很简单,执行传入的参数并且将之前两个函数当做参数传进去
- 要注意的是,可能执行函数过程中会遇到错误,需要捕获错误并且执行
reject
函数
最后我们来实现较为复杂的then
函数
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected == 'function' ? onRejected : r => {
throw r
}
if (that.state === PENDING) {
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallbacks.push(onRejected)
}
if (that.state === RESOLVED) {
onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}
- 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
- 当参数不是函数类型时,需要创建一个函数赋值为对应的参数,同时也实现了透传,如下代码
// 该代码目前在简单版中会报错,只是作为一个透传的例子
Promise.resolve(4).then().then((value) => console.log(value))
- 接下来就是一系列判断状态的逻辑,当状态不是等待时,就去执行相对应的函数。如果状态是等待态的话,就往回调函数中
push
函数,比如如下代码就会进入等待态的逻辑
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
},0)
}).then(value => {
console.log(value)
})
以上就是简单版Promise
的实现,最终代码如下:
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function MyPromise(fn) {
const that = this
that.state = PENDING
that.value = null
that.resolvedCallbacks = []
that.resolvedCallbacks = []
function resolve(value) {
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value
that.resolvedCallbacks.map(cb => cb(that.value))
}
}
function reject(value) {
if (that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map(cb => cb(that.value))
}
}
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : r => {
throw r
}
if (that.state === PENDING) {
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallbacks.push(onRejected)
}
if (that.state === RESOLVED) {
onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}
// 此匿名函数接受两个参数resolve,reject
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
},0)
}).then(value => {
console.log(value)
})
Promise A+ 唯有多写
// then执行比Promise晚
// reject 拒绝当前服务
// 完善Promise状态 pending ---> fulfilled
// promise.all 所有的promise进行调用 原子操作
// race 只要有一个状态发生变化值就返回
function Promise (fn) {
var callback;
this.then = function (done) {
// callback赋值为回调函数function
callback = done
}
// 先执行resolve
function resolve (data) {
setTimeout(function () {
// 先执行了then,geicallback赋值
callback(data)
},0)
}
fn(resolve)
}
使用
new Promise(function (resolve, reject) {
resolve("解决")
}).then(function (data) {
console.log(data)
})
new Promise(function (resolve, reject) {
resolve("解决")
// 不会执行
setTimeout( () => {
resolve("解决1")
}, 2000)
}).then(function (data) {
console.log(data)
})
// 解决
console.log(1)
new Promise(resolve => {
console.log(2)
resolve()
console.log(3)
}).then(() => {
console.log(4)
})
console.log(5)
// 1 2 3 4 5
注意,调用resolve
或reject
并不会终结Promise
的参数函数的执行。
上面代码中,调用resolve()
后,console.log(3)
还是会执行。
因此,最好在resolve
或reject
前面加上return
,这样就不会继续执行了。
Promise.prototype.finally()
finally
方法用于指定不管Promise
对象最后状态如何,都会执行的操作。
Promise.all()
方法用于将多个Promise
实例,包装成一个新的Promise
实例。
二、Promise.resolve()
Promise.resolve(value)
方法返回一个以给定值value
解析后的Promise
对象。如果该值为promise
,返回这个promise
;如果这个值是thenable
(即带有"then"方法
),返回的promise
会"跟随"这个thenable
的对象,采用它的最终状态;否则返回的promise
将以此值完成。此函数将类promise
对象的多层嵌套展平。
Promise.resolve('foo')
.then(Promise.resolve('bar'))
.then(function (result) {
console.log(result)
})
// foo
var promise1 = Promise.resolve(123);
promise1.then(function(value) {
console.log(value); // 123
});
// 使用静态Promise.resolve方法
Promise.resolve("Success").then(function(value) {
console.log(value); // "Success" 此时的状态机为resolve
}, function(value) {
// 不会被调用
});
// resolve一个数组
var p = Promise.resolve([1,2,3]);
p.then(function(v) {
console.log(v[0]); // 1
});
Resolve另一个promise
此方法和Promise.resolve()嵌套 async await 一个道理
var original = Promise.resolve(33)
var cast = Promise.resolve(original)
cast.then(value => {
console.log('value' + value)
})
console.log('original === cast' + (original === cast))
/*
* 打印顺序如下,这里有一个同步异步先后执行的区别
* original === cast ? true
* value: 33
*/
从上面的打印结果说明promise
嵌套promise
之后的值和promise
是等价的,因此在KOA2
中即使外层嵌套Promise.resolve()
但并不影响结果。Promise.resolve()方法是返回resolve
的状态机。
// Resolve一个thenable对象
var p1 = Promise.resolve({
then: function(onFulfill, onReject) { onFulfill("fulfilled!")}
})
console.log(p1 instanceof Promise) // true, 这是一个Promise对象
p1.then(function(v) {
console.log(v); // 输出"fulfilled!"
}, function(e) {
// 不会被调用
})
// Thenable在callback之前抛出异常
// Promise rejects
var thenable = { then: function(resolve) {
throw new TypeError('Throwing')
resolve("Resolving")
}}
var p2 = Promise.resolve(thenable);
p2.then(function(v) {
// 不会被调用
}, function(e) {
console.log(e); // TypeError: Throwing
})
// Thenable在callback之后抛出异常
// Promise resolves
var thenable = { then: function(resolve) {
resolve("Resolving");
throw new TypeError("Throwing");
}}
var p3 = Promise.resolve(thenable);
p3.then(function(v) {
console.log(v); // 输出"Resolving"
}, function(e) {
// 不会被调用
});
三、Promise.all()
Promise.all(iterable)方法返回一个Promise
实例,此实例在iterable
参数内所有的promise
都"完成(resolved
)"或参数中不包含promise
时回调完成(resolve
);如果参数中promise
有一个失败(rejected
),此实例回调失败(reject
),失败原因的是第一个失败promise
的结果。
var promise1 = Promise.resolve(3)
var promise2 = 42
var promise3 = new Promise((resolve, reject) {
// setTimeout第三个以后的参数是作为第一个func()的参数传进去。
setTimeout(resolve, 100, 'foo')
})
Promise.all([promise1, promise2, promise3]).then((value) {
console.log(values) // [3, 42, "foo"]
})
- 返回值
- 如果传入的参数是一个空的可迭代对象,则返回一个
已完成(already resolved)
状态的Promise
。 - 如果传入的参数不包含任何
promise
,则返回一个异步完成(asynchronously resolved) Promise
。注意:Google Chrome 58 在这种情况下返回一个已完成(already resolved)
状态的Promise
。 - 其它情况下返回一个
处理中(pending)
的Promise
。这个返回的promise
之后会在所有的promise
都完成或有一个promise
失败时异步地变为完成或失败。 见下方关于“Promise.all 的异步或同步”
示例。返回值将会按照参数内的promise
顺序排列,而不是由调用promise
的完成顺序决定。
// setTimeout
var doc=document.getElementById('div');
setTimeout(function(){
doc.style.color='red';
},10000,setTimeout(function(){
doc.style.color='black';
},5000));
上面的结果是,div元素内的字体样式5秒后变黑,10秒后再变红。是不是很惊奇,因为第三个参数也是一个定时器,5后就会开启。和JQuery里面的animate()不同,animate里面回调是执行了前面之后再执行后面的。
说明
-
此方法在集合多个
promise
的返回结果时很有用。 -
完成(
Fulfillment
): 如果传入的可迭代对象为空,Promise.all
会同步地返回一个已完成(resolved
)状态的promise
。 如果所有传入的promise
都变为完成状态,或者传入的可迭代对象内没有promise
,Promise.all
返回的promise
异步地变为完成。 在任何情况下,Promise.all
返回的promise
的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非promise
值)。 -
失败/拒绝(
Rejection
): 如果传入的promise
中有一个失败(rejected
),Promise.all
异步地将失败的那个结果给失败状态的回调函数,而不管其它promise
是否完成。 -
实例
-
Promise.all
的使用 如果参数中包含非promise
值,这些值将被忽略,但仍然会被放在返回数组中(如果promise
完成的话):
var p = Promise.all([1,2,3]) // Promise { <state>: "fulfilled", <value>: Array[3] }
p.then(v => console.log(v)) // [1, 2, 3]
var p2 = Promise.all([1,2,3, Promise.resolve(444)]) // Promise { <state>: "fulfilled", <value>: Array[4] }
var p3 = Promise.all([1,2,3, Promise.reject(555)]) // Promise { <state>: "rejected", <reason>: 555 }
参考文档: