Promise是什么
Promise 是异步编程的一种解决方案。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
常见传统异步解决方案
1、回调方法
2、事件
3、发布-订阅模式
4、观察者模式
5、其它...
Promise的优势
比传统的解决方案——更合理和更强大。
已逐渐成为现在前端编程中的一种良好编码实践,使其代码结构更加合理、可读性更强。
分步:实现自己的Promise
第一步:从promise的用法中收集它的特点
/*******************************************************************************
- 分析总结:
- 1、Promise是一个类, 接受一个执行函数(excurtor),此执行函数会立即执行
- 2、excurtor函数接受两个方法参数, 分别为resolve、reject;调用时可以传入一个值,此值可以是任意值
- 3、调用resolve,会执行then方法的成功回调(第一个参数)
- 4、调用reject,会执行then方法的失败回调(第二个参数)
- 5、promise有三种状态:pending(初始状态)、resolved、rejected;
- 只能从pending变为resolved或rejeted, 状态一旦确定,不能再次改变
- 6、每个实例都有then方法,可以多次then, then方法的参数个数(2个,1个,0个),当then为空时,会将状态传递下去,给下一次then
- 7、调用then时,如果已成功,则执行成功回调,并把成功的内容传递过去;反之,失败亦然;
- 8、如果类的执行函数执行抛出异常时,则会走失败回调,且传入异常
- 9、调用then时,会返回一个新的promise,实现链式调用;
- 10、then执行时,如果回调有返回结果,则会执行下一次的成功回调,并把值传入;如果抛出了异常,则会走下一次的失败回调
- 11、失败了也可以再成功,如没有返回值,则返回undefined
- 12、catch会捕获没有捕获到的错误
- 13、如果回调中返回的是一个promise, 会等待其执行,来决定走下一次的成功还是失败回调
- 14、自己不能等待自己完成,否则会报一个“循环引用”的类型错误
- 15、为什么链接调用时不能返回this? 因为状态确定后就不能再改变 ******************************************************************************/
第二步:Promise骨架 (简洁版)
/* ----------------------------------------------------------------------------
* Promise 骨架板
--------------------------------------------------------------------------- */
const states = {
PENDING : 'pending',
FULFILLED: 'fulfilled',
REJECTED : 'rejected'
}
class Promise {
/**
* [ 构造函数 ]
* @param {[function]} excurtor [执行函数]
* @return {[undefined]} undefined
*/
constructor (excurtor) {
let self = this;
this.status = states.PENDING; // 当前状态
this.value = undefined; // 成功的内容
this.reason = undefined; // 失败的原因
this.onFulfilled = []; // 存储成功回调
this.onRejected = []; // 存储失败回调
// 成功方法
function resolve (value) {
let onFulfilled = self.onFulfilled;
// 判断状态是否为pending,只有此状态时,才可以发生状态改变
if (self.status === states.PENDING) {
self.status = states.FULFILLED;
self.value = value;
if (onFulfilled.length) {
onFulfilled.forEach(fn => {
fn();
});
}
}
}
// 失败方法
function reject (reason) {
let onRejected = self.onRejected;
// 判断状态是否为pending,只有此状态时,才可以发生状态改变
if (self.status === states.PENDING) {
self.status = states.REJECTED;
self.reason = reason;
if (onRejected.length) {
onRejected.forEach(fn => {
fn()
});
}
}
}
// 立即调用执行函数,并捕获异常
try {
excurtor(resolve, reject);
} catch (e) {
reject(e); // 捕获到执行函数异常后,直接执行失败回调
}
}
/**
* [ 实例方法: then ]
* @param {[function]} onFulfilled [成功回调]
* @param {[function]} onRejected [失败回调]
* @return {[undefined]} undefined
*/
then (onFulfilled, onRejected) {
let self = this;
// 状态已改变为成功时,则立即执行
if (self.status === states.FULFILLED) {
onFulfilled(self.value);
}
// 状态已改变为失败时,则立即执行
if (self.status === states.REJECTED) {
onRejected(self.reason);
}
// 处理异步情况,先存在起来,等状态改变再执行相应回调
if (self.status === states.PENDING) {
self.onFulfilled.push(() => {
onFulfilled(self.value);
});
self.onRejected.push(() => {
onRejected(self.reason);
});
}
}
}
// 测试例子 ---------------------------------------------------------------------
let promise = new Promise((resolve, reject) => {
//setTimeout(() => {
resolve(100);
//}, 1000)
});
promise.then(
value => console.log(value),
reason => console.log(`fail: ${ reason }`)
);
复制代码
第三步:Promise完整版
/* ----------------------------------------------------------------------------
* Promise 完整版
--------------------------------------------------------------------------- */
const states = {
PENDING : 'pending',
FULFILLED: 'fulfilled',
REJECTED : 'rejected'
}
class Promise {
/**
* [ 构造函数 ]
* @param {[Function]} excurtor [执行函数]
* @return {[Undefined]} undefined
*/
constructor (excurtor) {
let self = this;
this.status = states.PENDING; // 当前状态
this.value = undefined; // 成功的内容
this.reason = undefined; // 失败的原因
this.onFulfilled = []; // 存储成功回调
this.onRejected = []; // 存储失败回调
// 成功方法
function resolve (value) {
let onFulfilled = self.onFulfilled;
// 判断状态是否为pending,只有此状态时,才可以发生状态改变
if (self.status === states.PENDING) {
self.status = states.FULFILLED;
self.value = value;
if (onFulfilled.length) {
onFulfilled.forEach(fn => {
fn();
});
}
}
}
// 失败方法
function reject (reason) {
let onRejected = self.onRejected;
// 判断状态是否为pending,只有此状态时,才可以发生状态改变
if (self.status === states.PENDING) {
self.status = states.REJECTED;
self.reason = reason;
if (onRejected.length) {
onRejected.forEach(fn => {
fn()
});
}
}
}
// 立即调用执行函数,并捕获异常
try {
excurtor(resolve, reject);
} catch (e) {
reject(e); // 捕获到执行函数异常后,直接执行失败回调
}
}
/**
* [ 实例方法: then ]
* @param {[Function]} onFulfilled [成功回调]
* @param {[Function]} onRejected [失败及异常回调]
* @return {[Object]} promise2
*/
then (onFulfilled, onRejected) {
// 补全参数,实现往下传递
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : error =>{ throw error };
let self = this;
let promise2;
promise2 = new Promise((resolve, reject) => {
// 状态已改变为成功时,则立即执行
if (self.status === states.FULFILLED) {
setTimeout(() => { // 此定时器确保回调方法在异步中执行的,也方便进行promiseA+规范测试
// try catch 用来捕获回调方法中抛出的异常
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
// 状态已改变为失败时,则立即执行
if (self.status === states.REJECTED) {
setTimeout(() => { // 同上
// 同上
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
// 处理异步情况,先存在起来,等状态改变再执行相应回调
if (self.status === states.PENDING) {
self.onFulfilled.push(() => {
setTimeout(() => { // 同上
// 同上
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
self.onRejected.push(() => {
setTimeout(() => { // 同上
// 同上
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
/**
* [ 实例方法:catch ]
* @param {[Function]} onRejected [失败及异常回调]
* @return {[Object]} promise
*/
catch (onRejected) {
return this.then(null, onRejected);
}
/**
* [ 静态方法(由类调用):all ]
* @param {[Array]} promises [由promise组成的数组]
* @return {[Object]} promise
*/
static all (promises) {
let res = [];
let count = 0;
let length = promises.length;
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
promise.then(
value => {
res[index] = value;
if (++count === length) {
resolve(res);
}
},
reject
);
});
});
}
/**
* [ 静态方法(由类调用)race ]
* @param {[Array]} promises [由promise组成的数组]
* @return {[Object]} promise
*/
static race (promises) {
return new Promise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject);
})
});
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 自己不能等待自己的状态改变
if (promise2 === x) {
return reject(new TypeError('循环引用'));
}
let called; // 标识位,要么成功,要么失败;
let typeX = typeof x;
// 判断x的类型是否为对象或者函数且不为null
if (x !== null && (typeX === 'object' || typeX === 'function')) {
try {
let then = x.then; // 获取then方法; 此处的try用于防止第三方库中获取值会抛错
if (typeof then === 'function') { // 说明返回的是promise事例
// 执行then方法,并将this指向x
then.call(x, y => {
if (called) return;
called = true;
// 如果返回的还是promise, 则将会继续递归调用
resolvePromise(promise2, y, resolve, reject);
}, e => {
if (called) return;
called = true;
reject(e);
});
} else { // x为变通对象时,直接成功
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
// 为了方便promiseA+规范测试
Promise.defer = Promise.deferred = function () {
let dtd = {};
dtd.promise = new Promise((resolve, reject) => {
dtd.resolve = resolve;
dtd.reject = reject;
});
return dtd;
};
module.exports = Promise;
// 测试例子 ---------------------------------------------------------------------
let promise = new Promise((resolve, reject) => {
resolve(1000);
//reject(100);
});
promise
.then(
value => {
console.log('then:', value);
return new Promise((resolve, reject) => {
//resolve(199);
reject('error123');
});
},
reason => console.log(`fail: ${ reason }`)
)
.then(
value => console.log(value),
reason => console.log(`fail: ${ reason }`)
);
Promise.all([
new Promise((resolve, reject) => {
resolve(1);
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 1000);
}),
new Promise((resolve, reject) => {
resolve(3);
//reject('出错了')
})
]).then(
value => console.log(value),
e => console.log(e)
);
复制代码
Promise/A+ 参考资料及测试插件
1、PromiseA+规范说明网址
2、promises-aplus-tests
简介:此包模块可以帮助我们测试自已写的Promise是否符合上面的规范。
命令: promises-aplus-tests promise.js
说明:命令后的文件名(promise.js) 为你自己定义的文件名