以下内容来自拉勾教育大前端高薪训练营 JavaScript深度剖析的一个模块的学习笔记,也加入了自己一些总结。
先来看一个最简单的Promise的使用
const p1 = new Promise((resolve, reject) => {
resolve('success')
})
p1.then((res) => console.log('res'), err => console.log(err))
// success
Promise
就是一个类,在执行这个类的时候,需要传递一个执行器进去,执行器会立即执行Promise
中有三种状态,分别为 成功 fulfilled
,失败 rejected
, 等待 pending
,pending -> fulfilled
, pending -> rejected
, 一旦状态确定就不可更改resolve
和reject
函数是用来更改状态的 resolve: fulfilled
, reject: rejected
我们可以写出Promise的大致结构
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
constructor (executor) {
executor(this.resolve, this.reject)
}
// promise 状态
status = PENDING;
// 成功后的值
value = undefined;
// 失败后的值
reason = undefined;
resolve = () => {
// 如果状态不是等待,阻止程序向下运行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
}
reject = () => {
// 如果状态不是等待,阻止程序向下运行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
}
then (successCallback, failCallback) {
// 判断状态
if (this.status === FULFILLED) {
successCallback();
} else if (this.status === REJECTED) {
failCallback()
}
}
}
测试一下
let p1 = new MyPromise((resolve, reject) => {
resolve('成功')
})
p1.then((value) => {
console.log(value)
}, reason => {
console.log(reason)
})
// 成功
Promise A+规范中文
Promise A+规范英文
根据规范我们可以总结几条核心的规则,更多的规范我们可以查看文档进行总结~
Promise
的当前状态必须为以下三种状态中的一种:等待态(Pending)
、执行态(Fulfilled)
和拒绝态(Rejected)
。promise.then(onFulfiled, onRejected)
下面我们将一步步来实现Promise
// 成功回调
successCallback = undefined;
// 失败后的回调
faliCallback = undefined;
resolve = value => {
// 判断成功回调是否存在,如果存在 调用
this.successCallback && this.successCallback(this.value)
}
reject = reason => {
// 判断失败回调是否存在,如果存在则调用
this.faliCallback && this.faliCallback(this.reason)
}
then (successCallback, faliCallback) {
// 判断状态
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
faliCallback(this.reason);
} else {
// 等待
// 将等待回调和失败回调存储起来
this.successCallback = successCallback;
this.faliCallback = faliCallback;
}
}
测试一下
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000)
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
// success
successCallback = [];
failCallback = [];
// 将原来代码修改为
// this.successCallback && this.successCallback(this.value);
while (this.successCallback.length) this.successCallback.shift()(this.value);
// this.faliCallback && this.faliCallback(this.reason);
while (this.faliCallback.length) this.faliCallback.shift()(this.reason);
测试一下
let promise = new MyPromise((resolve, reject) => {
// resolve('大白菜~~')
// reject('失败')
setTimeout(() => {
resolve('大白菜~')
}, 2000)
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
// 大白菜
Promise
的then
方法是可以链式调用的,后面的then
方法回调函数拿到的值实际上是拿到上一个then
方法回调函数的返回值then
方法的链式调用, then
方法是Promise
对象下面的,如果要实现链式调用,那么每一个then
方法都应该返回一个Promise
对象注意
在链式调用then
方法的时候,回调函数可以返回一个普通值,和一个promise
对象
如果返回的是普通值,我们可以直接调用resolve(x)
把这个普通值传递给下一个promise
对象
如果是promise
对象的话,我们需要查看返回的promise
对象状态,如果状态是成功的,我们需要调用resolve
方法,把成功的状态传递给它,
如果是失败的,需要把reject
传递给下一个promise
对象
// 改造then方法
then (successCallback, faliCallback) {
let promise2 = new myPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
let x = successCallback(this.value);
// 判断x的是是普通值还是promise对象
// 如果是普通值, 直接调用resolve
// 如是是promise对象 查看promise对象返回的结果
// 在根据promise对象返回的结果 决定调用resovle还是reject
resolvePromise(x, resolve, reject)
// resolve(x)
} else if (this.status === REJECTED) {
faliCallback(this.reason);
} else {
// 等待
// 将等待回调和失败回调存储起来
this.successCallback.push(successCallback);
this.faliCallback.push(faliCallback);
}
});
return promise2;
}
// 定义resolvePromise方法
function resolvePromise(x, resolve, reject) {
if (x instanceof myPromise) {
// promise对象
x.then(resolve, reject);
} else {
// 普通值
}
}
// 测试一下
let promise = new MyPromise((resolve, reject) => {
resolve('大白菜~~')
// reject('失败')
// setTimeout(() => {
// resolve('大白菜~')
// }, 2000)
})
function other () {
return new MyPromise((resolve, reject) => {
resolve('other');
})
}
promise.then(value => {
console.log(value);
return other();
}).then(value => {
console.log(value)
})
// 大白菜
// other
当链式调用Promise
对象下面的then
方法的时候, 在then
方法回调函数中可以返回Promise
对象,但我们需要考虑另外一种情况,在then
方法回调函数中不能返回当前这个then
方法他所返回的Promise
对象, 如果返回了then
方法返回的Promise
对象,就会发生循环调用。
示例
let promise = new Promise((resolve, reject) => {
resolve('大白菜')
})
let p1 = promise.then((value) => {
console.log(value)
return p1
})
// 报错
// TypeError: Chaining cycle detected for promise #
解决
then
方法中返回的 Promise
对象就是promise 2Promise
对象就是 xpeomise2
与 x
是否相等,reject
改造代码
// 将then方法里面改造成异步代码加入setTimeout()
setTimeout(() => {
// 执行成功调用成功回调函数,拿到返回值
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
}, 0)
// 改造resolvePromise方法
function resolvePromise (promise2, x, resolve, reject) {
// 判断是否相等
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #' ))
}
if (x instanceof MyPromise) {
// promise 对象
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
测试一下
let promise = new MyPromise((resolve, reject) => {
resolve('大白菜')
})
let p1 = promise.then((value) => {
console.log(value)
return p1
})
p1.then((value) => {
console.log(value)
}, (err) => {
console.log(err)
})
// 大白菜
// Chaining cycle detected for promise #
在执行构造器中加入try catch
constructor (executor) {
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e);
}
}
// 测试一下
let promise = new myPromise((resolve, reject) => {
throw new Error('executor error')
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason.message)
})
// 成功捕获错误 excutor error
then 回调函数捕获错误
// 在then方法的setTimeout 中添加try catch
setTimeout(() => {
try {
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
// 测试
let promise = new MyPromise((resolve, reject) => {
resolve('大白菜')
})
promise.then((value) => {
console.log(value)
throw new Error('then error')
}, (err) => {
console.log(err.message)
}).then((value) => {
console.log(value)
}, reason => {
console.log('~~~')
console.log(reason.message)
})
// 大白菜
// ~~~
// then error
修改失败的地方
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
// 测试一下
let promise = new MyPromise((resolve, reject) => {
reject('失败')
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
return '大白菜~~';
}).then(value => {
console.log(value)
})
// 失败
// 大白菜~~
当是异步的时候
将原来的代码改成
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
// 然后resolve和reject就不需要传值了
while (this.successCallback.length) this.successCallback.shift()();
while (this.failCallback.length) this.failCallback.shift()();
测试一下
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success~~~~');
// reject('error~~~~')
}, 2000)
})
promise.then((value) => {
console.log(value)
return '米糕';
}, reason => {
console.log(reason)
return '大白菜';
}).then((value) => {
console.log(value);
})
// suceess~~~
// 米糕
到这里Promise的核心功能就基本已经实现啦~~~
let promise = new Promise((resolve, reject) => {
resolve(100)
})
promise
.then()
.then()
.then(value => {
cosole.log(value)
})
修改Promise的代码
在then方法我们判断successCallback和failCallback是否存在
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => { throw reason };
测试一下
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success');
// reject('reject');
}, 2000)
})
promise.then().then().then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
// success
// reject
Promise
对象,数组的顺序一定是得到结果的顺序Promise.all
特点在all
中的所有Promise
对象, 如果他的状态都是成功的,那么 all
方法就是成功的,如果有一个是失败的, 那么就是失败的all
所以 all
是一个静态方法Promise.all
是解决异步并发问题, 允许按照异步代码调用的顺序得到异步代码执行的结果, 由于all
方法是静态方法, all
前面定义 static
关键字, all 方法接收一个数组作为参数, all
方法的返回值也是一个Promise
对象, 在Promise
对象中通过循环 传递的数组,在循环的过程判断是普通值,还是Promise 对象, 进行不同的调用Promise
失败,那么Promise.all
返回的Proise
对象失败static all(array) {
let result = []
let index = 0
return new MyPromise((resolve, reject) => {
// 执行for 循环有异步操作,循环没有等待异步操作。
// 如果index 等于 array的length 就调用resolve
function addData (key, value) {
result[key] = value;
index++
if (index === array.length) {
resolve(result)
}
}
// 需要判断是普通值, 还是Promise 对象
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof MyPromise) {
// promise 对象
current.then(value => addData(i, value), reason => reject(reason) )
} else {
// 普通值
addData(i, array[i])
}
}
})
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1000)
})
}
function p2() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1000)
})
}
MyPromise.all(['a', 'b', p1(), p2(), 'c']).then(result => {
console.log(result)
})
// [ 'a', 'b', 'p1', 'p1', 'c' ]
Promise.resolve
的作用是将给定的值转换为Promise
对象, 也就是说Promise.resolve
的返回值就就是一个Promise对象,在返回的Promise
对象中会包裹给定的这个值resolve
的内部, 会创建一个Promise
对象,并把这个值包裹在Promise
对象中,然后把创建出来的Promise
对象最作为resolve
的返回值,正是因为这样,我们才能后面进行链式调用then
方法, 通过then方法的成功回调函数来拿到这个值, Promise.resolve
也可以接收一个Promise
对象, 在Promise.resolve
内部会判断给定的值是普通值还是 Promise
对象,如果是Promise
对象的话,会原封不动把Promise
在作为Promise.resolve
的返回值,所以才能在后在调用then
方法,通过then方法成功回调函数来拿到Promise
对象的返回值static resolve(value) {
if (value instanceof myPromise) return value;
return new MyPromise(resolve => resolve(value))
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1000)
})
}
MyPromise.resolve('大白菜').then(value => console.log(value))
MyPromise.resolve(p1()).then(value => console.log(value))
// 大白菜
// p1
static reject(reason) {
return new MyPromise((resolve,reject) => reject(reason))
}
Promise.finally有两个特点
Promise
对象最终的状态是成功还是失败,finally
方法这个会回调函数始终都会执行一次finally
的后面可以链式调用then
方法来拿到当前这个Promise
对象最终返回的结果finally(callback) {
return this.then(value => {
callback();
return value
}, reason => {
callback();
throw reason
})
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
resolve('p1 reject')
// reject('p1 reject')
})
}
function p2() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 2000)
})
}
p1().finally(() => {
console.log('finally');
// return p2();
}).then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
// finally
// p1 reject
finally
的回调函数中,其实可以在return
一个Promise
对象resolve
方法callback
返回的是普通值,转换Promise
对象, 等待Promise
对象执行完成,如果返回的是Promise
对象,还等待你执行完成,在返回valuefinally(callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value);
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason },);
})
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1 resolve')
}, 2000)
})
}
function p2() {
return new MyPromise((resolve, reject) => {
resolve('p2 resolve')
// reject('p2 reject')
})
}
p2().finally(() => {
console.log('finally');
return p1()
}).then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
// finally
// 等待2s后执行 输出p2 resolve
catch
方法的作用是用来处理当前这个Promise
对象最终状态为失败的情况的,就是说当我们调用then方法时候,我们可以不传递失败回调, 如果不传失败回调,那么失败回调就可以被catch捕获,从而去执行传入到catch方法的回调函数catch (failCallback) {
return this.then(undefind, failCallback);
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
// resolve('大白菜~~')
reject('error')
})
}
p1()
.then(value => console.log(value))
.catch(reason => console.log(reason))
Promise.race
只返回第一个执行完毕的Promise
的结果,无论结果是fullfilled
还是rejected
static race(promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
return
} else {
for (let p of promises) {
MyPromise.resolve(p).then(value => {
resolve(value)
}, reason => {
reject(reason)
})
}
}
})
}
测试一下
const MyPromise = require('./myPromise');
let promise1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('大白菜')
}, 2000)
});
let promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('米糕')
}, 1000)
});
MyPromise.race([promise1, promise2]).then((value) => {
console.log(value);
});
// 米糕
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
constructor (executor) {
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e);
}
}
// promise 状态
status = PENDING;
// 成功后的值
value = undefined;
// 失败后的值
reason = undefined;
// 成功回调
successCallback = [];
// 失败后的回调
failCallback = [];
resolve = value => {
// 如果状态不是等待,阻止程序向下运行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
// 判断成功回调是否存在,如果存在 调用
// this.successCallback && this.successCallback(this.value);
while (this.successCallback.length) this.successCallback.shift()();
}
reject = reason => {
// 如果状态不是等待,阻止程序向下运行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// 判断失败回调是否存在,如果存在则调用
// this.faliCallback && this.faliCallback(this.reason);
while (this.failCallback.length) this.failCallback.shift()();
}
then (successCallback, failCallback) {
// 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
successCallback = typeof successCallback === 'function' ? successCallback : value => value;
failCallback = typeof failCallback === 'function' ? failCallback : reason => { throw reason };
let promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = successCallback(this.value);
// 判断x的是是普通值还是promise对象
// 如果是普通值, 直接调用resolve
// 如是是promise对象 查看promise对象返回的结果
// 在根据promise对象返回的结果 决定调用resovle还是reject
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
} else {
// 等待
// 将等待回调和失败回调存储起来
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}) // 将成功回调推入
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}) // 将失败回调推入
}
});
return promise2;
}
static all(array) {
let result = []
let index = 0
return new MyPromise((resolve, reject) => {
// 执行for 循环有异步操作,循环没有等待异步操作。
// 如果index 等于 array的length 就调用resolve
function addData (key, value) {
result[key] = value;
index++;
// 保证all的每一项都执行完了
if (index === array.length) {
resolve(result);
}
}
// 需要判断是普通值, 还是Promise 对象
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof MyPromise) {
// promise 对象
current.then(value => addData(i, value), reason => reject(reason));
} else {
// 普通值
addData(i, array[i])
}
}
})
}
static resolve(value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value))
}
static reject(reason) {
return new MyPromise((resolve,reject) => reject(reason))
}
static race(promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
return
} else {
for (let p of promises) {
MyPromise.resolve(p).then(value => {
resolve(value)
}, reason => {
reject(reason)
})
}
}
})
}
// finally链式调用返回Promise
finally(callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value);
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason },);
})
}
catch (failCallback) {
return this.then(undefined, failCallback);
}
}
function resolvePromise (promise2, x, resolve, reject) {
// 判断是否相等
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #' ))
}
if (x instanceof MyPromise) {
// promise 对象
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
module.exports = MyPromise;
以上就是对Promise的实现的整个过程,我们首先从一个简单Promise的使用实例开始,对Promise的核心进行了分析,根据分析我们实现了大致的一个结构,然后根据Promise A+规范一步一步的进行填充代码。主要实现了
最后感谢您花宝贵的时间阅读本文,如果本文对你有帮助的话,给本文点个赞吧,您的肯定是我前进的最大动力~