上一节, 咱们介绍了js中promise的静态方法和实例方法, 以及使用. 本节将手写一个符合promiseA+规范的promise。javascript
新建一个xpromise.js的文件, 建一个class,取名为XPromise. 把静态方法和实例方法的签名加上.java
代码以下:git
class XPromise {
constructor(cb) {
}
then(resolve, reject) {
}
catch(reject) {
}
finally(cb) {
}
static resolve(val) {
}
static reject(reason) {
}
static all(promises) {
}
static race(promises) {
}
static allSettled(promises) {
}
static deferred() {
}
}
复制代码
接下来, 咱们实现每个方法.github
描述: 咱们先思考一下, Promise构造器里应该要作什么.npm
new Promise((resolve, reject) => {...})
复制代码
咱们就来实现以上功能, 代码以下:数组
// 定义promise的状态, 方便后面使用.
const s = {
pending: 'pending',
fulfilled: 'fulfilled',
rejected: 'rejected'
};
constructor(cb) {
// 设置初始状态为pending.
this.status = s.pending;
// 存储成功后的值.
this.value = null;
// 存储失败的缘由.
this.reason = null;
// 执行成功的回调搜集.
this.resolveCallbacks = [];
// 执行失败的回调搜集
this.rejectCallbacks = [];
const resolve = val => {
// 若是状态是pending, 就更改.
if (this.status === s.pending) {
this.status = s.fulfilled;
this.value = val;
this.resolveCallbacks.forEach(cb => cb())
}
};
const reject = reason => {
if (this.status === s.pending) {
this.status = s.rejected;
this.reason = reason;
this.rejectCallbacks.forEach(cb => cb());
}
};
cb(resolve, reject);
}
复制代码
描述: 先思考一下, then方法, 要作哪些事情promise
// then的使用
xx.then(result => {}, error => {})
xx.then('test', 'error occurrs')
复制代码
代码以下:bash
then(resolve, reject) {
// 对参数作检查.
// 可能的调用: .then('success', 'error')
// 若是不是一个方法, 就使用默认的函数.进行值传递便可.
const onResolved = typeof resolve === 'function' ? resolve : val => val;
const onRejected = typeof reject === 'function' ? reject : err => {throw err};
// 返回的是另外一个promise对象.
const newPromise = new XPromise((resolve, reject) => {
// 若是调用then的时候promise的状态已经变为完成.
// 那么调用成功的回调, 并传递参数.
if (this.status === s.fulfilled) {
// 使用setTimeout来模拟异步.
setTimeout(() => {
// 若是执行回调时, 发生异常.
// 那么就将异常做为promise失败的缘由.
try {
let result = onResolved(this.value);
// 调用resolvePromise函数, 更具result的值来决定newPromise的状态.
resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === s.rejected) {
// 使用setTimeout来模拟异步.
setTimeout(() => {
// 若是执行回调时, 发生异常.
// 那么就将异常做为promise失败的缘由.
try {
let result = onRejected(this.reason);
// 调用resolvePromise函数, 更具result的值来决定newPromise的状态.
resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
// 若是调用then时状态仍是pending. 说明promise执行
// 内部的resolve或reject是异步的. 须要把then中的成功回调和失败回调
// 先存储起来, 等等promise的状态改成成功或失败的时候再执行.
if (this.status === s.pending) {
this.resolveCallbacks.push(() => {
setTimeout(() => {
try {
const result = onResolved(this.value);
resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0)
});
this.rejectCallbacks.push(() => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return newPromise;
}
/** * 处理then中的返回结果. 方便链式调用. * @param {*} promise * @param {*} result * @param {*} resolve * @param {*} reject * @returns {XPromise} 返回一个promise. */
const resolvePromise = (promise, result, resolve, reject) => {
// 参数判断.
// 判断result和promise是否为相同的引用.防止循环引用.
if (promise === result) {
return reject(new TypeError('循环引用了.'));
}
// 用来记录promise的状态是否改变过.
// 一旦改变就不能再次更改为其余的状态.
let isCalled;
if(result !== null &&
(typeof result === 'object' || typeof result === 'function')){
try {
const then = result.then;
if(typeof then === 'function'){
then.call(result, newResult => {
if(isCalled)return;
isCalled = true;
// 这个newResult可能依旧是一个promise对象, 因此要递归调用.
resolvePromise(promise, newResult, resolve, reject);
}, error => {
if(isCalled)return;
isCalled = true;
reject(error);
});
}else{
resolve(result);
}
} catch (error) {
if(isCalled)return;
isCalled = true;
reject(error);
}
}else{
// 若是是一个普通值 那么就直接把result做为promise
// 的成功的回调.
resolve(result);
}
};
复制代码
描述: catch方法相对简单,其实就是执行then函数的第二个参数.异步
catch(reject) {
// 实际执行的是then的第二个参数.
return this.then(null, reject);
}
复制代码
描述: 其实就是then在只是成功和失败的回调时都要执行.函数
finally(cb) {
// then的resolve和reject都要执行.
return this.then(cb, cb);
}
复制代码
描述: 返回一个promise, 执行成功的回调. 代码实现以下, 有没有以为很是简单.
static resolve(val) {
return new XPromise((resolve) => resolve(val));
}
复制代码
描述: 返回一个promise, 执行失败的回调
static reject(reason) {
return new XPromise((resolve, reject) => reject(reason));
}
复制代码
描述: 返回一个promise:
static all(promises) {
return new XPromise((resolve, reject) => {
const arr = [];
promises.forEach((p, i) => {
p.then(data => {
arr[i] = data;
}, reject);
});
resolve(arr);
});
}
复制代码
描述: 返回一个promise,一旦参数中某个promise执行成功或失败, 就当即执行对于的回调.
static race(promises) {
return new XPromise((resolve, reject) => {
promises.forEach((p) => {
// resolve后,状态不能改变
p.then(resolve, reject);
});
});
}
复制代码
描述: 返回一个promise对象, 给定的全部promise都被成功解析或被失败解析,而且每一个对象都描述每一个promise的结果.
static allSettled(promises) {
return new XPromise((resolve, reject) => {
const arr = [];
promises.forEach((p, i) => {
p.then(data => {
arr[i] = data;
}, reason => {
arr[i] = reason;
});
});
// 等待全部都执行完成后, 再返回.
resolve(arr);
});
}
复制代码
描述: 返回一个普通对象, 里面包含一个promise.
static deferred() {
const defer = {}
defer.promise = new XPromise((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
})
return defer
}
复制代码
const s = {
pending: 'pending',
fulfilled: 'fulfilled',
rejected: 'rejected'
};
/** * 出来then中的返回结果. 方便链式调用. * @param {*} promise * @param {*} result * @param {*} resolve * @param {*} reject * @returns {XPromise} 返回一个promise. */
const resolvePromise = (promise, result, resolve, reject) => {
// 参数判断.
// 判断result和promise是否为相同的引用.防止循环引用.
if (promise === result) {
return reject(new TypeError('循环引用了.'));
}
// 用来记录promise的状态是否改变过.
// 一旦改变就不能再次更改为其余的状态.
let isCalled;
if(result !== null &&
(typeof result === 'object' || typeof result === 'function')){
try {
const then = result.then;
if(typeof then === 'function'){
then.call(result, newResult => {
if(isCalled)return;
isCalled = true;
// 这个newResult可能依旧是一个promise对象, 因此要递归调用.
resolvePromise(promise, newResult, resolve, reject);
}, error => {
if(isCalled)return;
isCalled = true;
reject(error);
});
}else{
resolve(result);
}
} catch (error) {
if(isCalled)return;
isCalled = true;
reject(error);
}
}else{
// 若是是一个普通值 那么就直接把result做为promise
// 的成功的回调.
resolve(result);
}
};
class XPromise {
static resolve(val) {
return new XPromise((resolve) => resolve(val));
}
static reject(reason) {
return new XPromise((resolve, reject) => reject(reason));
}
/** * - 若是参数中全部都执行成功,那么此实例执行成功的回调 * - 若是参数中有一个执行失败, 那么此实例当即执行失败的回调. 失败的缘由是第一个promise失败的结果. * @param {Array} promises Promise集合. * @returns {XPromise} 返回一个promise. */
static all(promises) {
return new XPromise((resolve, reject) => {
const arr = [];
promises.forEach((p, i) => {
p.then(data => {
arr[i] = data;
}, reject);
});
resolve(arr);
});
}
static race(promises) {
return new XPromise((resolve, reject) => {
promises.forEach((p) => {
// resolve后,状态不能改变
p.then(resolve, reject);
});
});
}
static allSettled(promises) {
return new XPromise((resolve, reject) => {
const arr = [];
promises.forEach((p, i) => {
p.then(data => {
arr[i] = data;
}, reason => {
arr[i] = reason;
});
});
// 等待全部都执行完成后, 再返回.
resolve(arr);
});
}
static deferred() {
const defer = {}
defer.promise = new XPromise((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
})
return defer
}
constructor(cb) {
// 设置初始状态为pending.
this.status = s.pending;
// 存储成功后的值.
this.value = null;
// 存储失败的缘由.
this.reason = null;
// 执行成功的回调搜集.
this.resolveCallbacks = [];
// 执行失败的回调搜集
this.rejectCallbacks = [];
const resolve = val => {
// 若是状态是pending, 就更改.
if (this.status === s.pending) {
this.status = s.fulfilled;
this.value = val;
this.resolveCallbacks.forEach(cb => cb())
}
};
const reject = reason => {
if (this.status === s.pending) {
this.status = s.rejected;
this.reason = reason;
this.rejectCallbacks.forEach(cb => cb());
}
};
cb(resolve, reject);
}
then(resolve, reject) {
// 对参数作检查.
// 可能的调用: .then('success', 'error')
// 若是不是一个方法, 就使用默认的函数.进行值传递便可.
const onResolved = typeof resolve === 'function' ? resolve : val => val;
const onRejected = typeof reject === 'function' ? reject : err => {throw err};
// 返回的是另外一个promise对象.
const newPromise = new XPromise((resolve, reject) => {
// 若是调用then的时候promise的状态已经变为完成.
// 那么调用成功的回调, 并传递参数.
if (this.status === s.fulfilled) {
// 使用setTimeout来模拟异步.
setTimeout(() => {
// 若是执行回调时, 发生异常.
// 那么就将异常做为promise失败的缘由.
try {
let result = onResolved(this.value);
// 调用resolvePromise函数, 更具result的值来决定newPromise的状态.
resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === s.rejected) {
// 使用setTimeout来模拟异步.
setTimeout(() => {
// 若是执行回调时, 发生异常.
// 那么就将异常做为promise失败的缘由.
try {
let result = onRejected(this.reason);
// 调用resolvePromise函数, 更具result的值来决定newPromise的状态.
resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
// 若是调用then时状态仍是pending. 说明promise执行
// 内部的resolve或reject是异步的. 须要把then中的成功回调和失败回调
// 先存储起来, 等等promise的状态改成成功或失败的时候再执行.
if (this.status === s.pending) {
this.resolveCallbacks.push(() => {
setTimeout(() => {
try {
const result = onResolved(this.value);
resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0)
});
this.rejectCallbacks.push(() => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
resolvePromise(newPromise, result, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return newPromise;
}
catch(reject) {
// 实际执行的是then的第二个参数.
return this.then(null, reject);
}
finally(cb) {
// then的resolve和reject都要执行.
return this.then(cb, cb);
}
}
module.exports = XPromise;
复制代码
npm install -D promises-aplus-tests
复制代码
npx promises-aplus-tests ./xpromise.js
复制代码
测试结果以下:872个cases, 所有经过测试.