本篇文章是包教包会,和你实现一个Promise的第三篇文章。由于这个小系列旨在完成一个符合规范的Promise,并且这三篇文章有先后关系,若是没有看过前两篇,第三篇看起来会有些莫名其妙。要是没看过,仍是建议先看下前两篇。在这里:javascript
到目前为止,咱们的MyPromise虽然能够用了,可是还有一些地方不符合规范,再就是有些地方还有点问题,咱们来进行逐个修改。github
如今then方法的两个参数是没有默认值的,因此若是使用者在调用then方法时,没有传递参数,后面咱们使用onResolved或者onRejected时,程序会报onResolved/onRejected is not a function
,这会致使咱们的Promise被迫终止,因此要给它们加上默认值。npm
MyPromise.prototype.then = function (onResolved, onRejected) {
// 看这里,设置onResovled的默认值
if (typeof onResolved !== 'function') {
onResolved = function(value) {
return value
}
}
// 看这里,设置onRejected的默认值
if (typeof onRejected !== 'function') {
onRejected = function(reason) {
throw reason
}
}
let promise2
if (this.status === 'pending') {
promise2 = new MyPromise((resolve, reject) => {
function successFn(value) {
let x = onResolved(value)
resolve_promise(promise2, x, resolve, reject)
}
function failFn(reason) {
let x = onRejected(reason)
resolve_promise(promise2, x, resolve, reject)
}
this.resolvedCallbacks.push(successFn)
this.rejectedCallbacks.push(failFn)
})
}
if (this.status === 'fulfilled') {
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
let x = onResolved(this.data)
resolve_promise(promise2, x, resolve, reject)
})
})
}
if (this.status === 'rejected') {
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
let x = onRejected(this.reason)
resolve_promise(promise2, x, resolve, reject)
})
})
}
return promise2
}
复制代码
上面经过tyepof判断onResovled或者onRejected是否是function类型,若是不是,就给一个函数。这样,不只避免了不传值的状况,也解决了使用随便乱传参数的问题。数组
对于onResolved的方法,咱们声明一个函数直接返回value便可,对于onRejected的方法,咱们抛一个错误出来。这样promise2也就能够经过then方法拿到它们返回或者抛出的错误的。promise
咱们来看这里的代码,这里只是其中一处:bash
if (this.status === 'fulfilled') {
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
// 看这里看这里
let x = onResolved(this.data)
resolve_promise(promise2, x, resolve, reject)
})
})
}
复制代码
咱们知道,then方法执行时,用户会写两个函数onResovled和onRejected而且传进来,而后咱们调用它们来得到当前Promise实例resolve或者reject传递过来的值。可是问题来了,由于onResovled和onRejected是用户实现、咱们调用的,那用户写onResolved或者onRejected时写错了,里面语法有问题,执行的时候报错了,那咱们的程序也会终止掉。因此咱们要针对这种可能性作一个try...catch捕捉。异步
总共有四个地方须要try...catch:函数
MyPromise.prototype.then = function (onResolved, onRejected) {
if (typeof onResolved !== 'function') {
onResolved = function(value) {
return value
}
}
if (typeof onRejected !== 'function') {
onRejected = function(reason) {
throw reason
}
}
let promise2
if (this.status === 'pending') {
promise2 = new MyPromise((resolve, reject) => {
function successFn(value) {
// 这里要try...catch
try {
let x = onResolved(value)
resolve_promise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
function failFn(reason) {
// 这里要try...catch
try {
let x = onRejected(reason)
resolve_promise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
this.resolvedCallbacks.push(successFn)
this.rejectedCallbacks.push(failFn)
})
}
if (this.status === 'fulfilled') {
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
// 这里也要哦
try {
let x = onResolved(this.data)
resolve_promise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
if (this.status === 'rejected') {
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
// 这里也要哦
try {
let x = onRejected(this.reason)
resolve_promise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
return promise2
}
复制代码
总共四个地方,若是onResolved或者onRejected执行报错,会被catch到,而且直接用reject把promise2标记为失败状态。then方法改完了,下面构造函数也要小小改一下。
构造函数有个地方要修改:resolve和reject里的内容须要异步执行。 为啥要这样写,其实我也不清楚,由于从逻辑上来看即便不写彷佛也没毛病。可是不它们设置成异步执行的话,有6个测试用例通不过。
因此我去看了它的测试用例,你也能够看看,地址在这里 :它是这么测的:
specify("fulfilled after a delay", function (done) {
var d = deferred();
var isFulfilled = false;
d.promise.then(function onFulfilled() {
assert.strictEqual(isFulfilled, true);
done();
});
setTimeout(function () {
d.resolve(dummy);
isFulfilled = true;
}, 50);
});
复制代码
根据规范,我只要保证onResolved在resolve以后执行就行,因此如今写的代码没啥问题,但问题是它的测试用例,先声明了一个isFulfilled,而后使用setTimeout调用resolve并将isFulfilled置为true,并在onResolved判断一个isFulfilled为true。
可是,咱们是在resolve函数里面就已经调用resolve了,因此此时isFufilled仍是false,因此咱们须要给resolve和reject里的代码都异步执行才能经过这里的测试。
function MyPromise(executor) {
this.status = 'pending'
this.data = undefined
this.reason = undefined
this.resolvedCallbacks = []
this.rejectedCallbacks = []
let resolve = (value) => {
// 这里加个setTimeout
setTimeout(() => {
if (this.status === 'pending') {
this.status = 'fulfilled'
this.data = value
this.resolvedCallbacks.forEach(fn => fn(this.data))
}
})
}
let reject = (reason) => {
// 这里也加个setTimeout
setTimeout(() => {
if (this.status === 'pending') {
this.status = 'rejected'
this.reason = reason
this.rejectedCallbacks.forEach(fn => fn(this.reason))
}
})
}
executor(resolve, reject)
}
复制代码
测试很简单,有专门的测试工具。
npm install -g promises-aplus-tests
复制代码
同时还要将咱们的MyPromise暴露出去,并提供promise实例和resolve以及reject函数引用。
MyPromise.deferred = function() {
let dfd = {}
dfd.promise = new MyPromise(function(resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
try {
module.exports = MyPromise
} catch (e) {}
复制代码
咱们须要在MyPromise上写一个静态方法,执行后返回一个dfd对象。这个对象上有三个属性:
promise
: 一个咱们要测试的Promise实例resolve
: 这个实例的resolve函数reject
: 这个实例的reject函数好了,到这里看一下包括测试须要在内的全部的代码:
function MyPromise(executor) {
this.status = 'pending'
this.data = undefined
this.reason = undefined
this.resolvedCallbacks = []
this.rejectedCallbacks = []
let resolve = (value) => {
setTimeout(() => {
if (this.status === 'pending') {
this.status = 'fulfilled'
this.data = value
this.resolvedCallbacks.forEach(fn => fn(this.data))
}
})
}
let reject = (reason) => {
setTimeout(() => {
if (this.status === 'pending') {
this.status = 'rejected'
this.reason = reason
this.rejectedCallbacks.forEach(fn => fn(this.reason))
}
})
}
executor(resolve, reject)
}
MyPromise.prototype.then = function (onResolved, onRejected) {
if (typeof onResolved !== 'function') {
onResolved = function(value) {
return value
}
}
if (typeof onRejected !== 'function') {
onRejected = function(reason) {
throw reason
}
}
let promise2
if (this.status === 'pending') {
promise2 = new MyPromise((resolve, reject) => {
function successFn(value) {
try {
let x = onResolved(value)
resolve_promise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
function failFn(reason) {
try {
let x = onRejected(reason)
resolve_promise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
this.resolvedCallbacks.push(successFn)
this.rejectedCallbacks.push(failFn)
})
}
if (this.status === 'fulfilled') {
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onResolved(this.data)
resolve_promise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
if (this.status === 'rejected') {
promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolve_promise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
return promise2
}
function resolve_promise(promise2, x, resolve, reject) {
if (x === promise2) {
reject(new TypeError('Chaining cycle detected for promise'))
return
}
if (x instanceof MyPromise) {
x.then(function(v) {
resolve_promise(promise2, v, resolve, reject)
}, function(t) {
reject(t)
})
return
}
if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
let called = false
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, function resolvePromise(y) {
if (called) return
called = true
resolve_promise(promise2, y, resolve, reject)
}, function rejectPromise(r) {
if (called) return
called = true
reject(r)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
MyPromise.deferred = function() {
let dfd = {}
dfd.promise = new MyPromise(function(resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
try {
module.exports = MyPromise
} catch (e) {
}
复制代码
开始测试:
promises-aplus-tests MyPromise.js
复制代码
你将会看到:
872个测试用例所有经过!
接下来,咱们实现一些很是经常使用的方法,这些方法虽然不在规范里,可是它们平时也有用武之地!
这个方法有如下特色:
MyPromise.all = function(arr) {
return new MyPromise(function(resolve, reject) {
let result = new Array(arr.length)
let count = 0
for(let i=0; i<arr.length; ++i) {
let currentPromise = arr[i]
currentPromise.then(function(res) {
result[i] = res
count++
if (count === arr.length) {
resolve(result)
}
}, function(reason) {
reject(reason)
})
}
})
}
复制代码
很好理解,就很少讲了~
注意,这个方法是实例方法:
MyPromise.prototype.catch = function(failFn) {
this.then(null, function(reason) {
failFn(reason)
})
}
复制代码
很少说了
注意,这是一个静态方法:
MyPromise.race = function(arr) {
return new MyPromise(function(resolve, reject) {
arr.forEach(promise => {
promise.then(resolve, reject)
})
})
}
复制代码
完事~
直接返回一个成功状态的Promise,静态方法:
MyPromise.resolve = function(value) {
return new MyPromise(function(resolve, reject) {
resolve(value)
})
}
复制代码
直接返回一个失败状态的Promise实例,静态方法:
MyPromise.reject = function(reason) {
return new MyPromise(function(resolve, reject) {
reject(reason)
})
}
复制代码
后面咱们实现的这些方法是没有测试用例的哦~
好不容易完成了一个彻底符合规范的Promise,给它起一个帅气的名字吧,就叫Zoro吧。Zoro是《海贼王》里罗罗诺亚·索隆的英文名字,索隆在小时候曾经向古伊娜许诺要成为世界最强剑豪 ,而如今索隆刚拿到阎魔这把刀,在成为最强的道路上一路狂奔,因此我以为这个名字再合适不过了!
完成的zoro地址在这里 ,欢迎点赞,感谢您的阅读!
(完)