这是一个简版非规范的Promise实现,其中前置知识点比较重要,理解了前置知识点的内容,也就了解了Promise最基本的原理。数组
这小节的核心是:把函数名当变量用。promise
如下三点理解以后,就可以实现简单的Promise了。浏览器
举个栗子bash
let myFn = function () {};
复制代码
你能够直接调用异步
myFn();
复制代码
和下面的代码效果是同样的函数
function myFn() {
// code here...
}
myFn();
复制代码
let fnArray = [];
let fn1 = () => { console.log(1) };
let fn2 = () => { console.log(2) };
fnArray.push(fn1);
fnArray.push(fn2);
fnArray.forEach(cb => cb());
复制代码
这段代码的运行结果是ui
1
2
复制代码
function showYouFn(fn1, fn2) {
// 调用
fn1();
fn2();
}
let myFn1 = () => { console.log(3); };
let myFn2 = () => { console.log(4); };
showYouFn(myFn1, myFn2);
复制代码
回想一下:把函数名当变量用。this
下面是一个简陋得不像Promise的Promise实现spa
function MyPromise(fn) {
function resolve(value) {
console.log(value);
}
function reject(value) {
console.log(value);
}
fn(resolve, reject);
}
复制代码
如今你就能够用上自定义的Promise了prototype
new MyPromise((resolve, reject) => {
setTimeout(()=>{
resolve('你看到我啦!');
}, 2000);
});
复制代码
将会在2秒后输出
你看到我啦!
复制代码
下面的代码效果和上面的同样,写出来是为了再次强调:函数当变量用。
let promiseFn = (resolve, reject) => {
setTimeout(()=>{
resolve('你看到我啦!');
}, 2000);
};
new MyPromise(promiseFn);
复制代码
解释一下总体代码:
MyPromise
中的参数 fn
是须要用户传入的自定义函数(该函数须要接收两个参数)。
MyPromise
中的内部还有两个函数 resolve
和 reject
,其中 resolve
表示用户的异步任务成功时应该调用的函数,reject
表示用户的异步任务失败时该调用的函数。
那用户如何调用 resolve
和 reject
呢?
很简单,把两个函数看成 fn
的参数传递出去便可。
因此 MyPromise
内部在调用 fn
时会把 resolve
和 reject
看成参数传递给 fn
。
而后用户在自定义函数内调用 resolve
或 reject
来通知 MyPromise
异步任务已经执行完了。
建议复制下面的代码去控制台试试
function MyPromise(fn) {
function resolve(value) {
console.log(value);
}
function reject(value) {
console.log(value);
}
fn(resolve, reject);
}
new MyPromise((resolve, reject) => {
setTimeout(()=>{
resolve('你看到我啦!');
}, 2000);
});
复制代码
经过上面的代码能够发现一个问题,咱们不知道Promise的异步任务进行到哪一步了、是成功仍是失败了。
因此增长三个状态用来标识一个Promise的异步任务进行到何种程度了。
pending
、resolved
、rejected
分别表示 执行中、已完成、已失败。
而后经过观察用户调用的是 resolve
仍是 reject
能够判断当前Promise的状态。
那么会有三种状况:
resolve
或 reject
以前状态是 pending
resolve
时,状态将变为 resolved
reject
时,状态将变为 rejected
下面进行代码的改造,定义了三个常量表示状态以及一个变量 state
用来存储当前状态。
而且当 resolve
被调用时将 state
修改成 resolved
。
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function MyPromise(fn) {
const that = this;
// 初始时状态为执行中,即pending
that.state = PENDING;
function resolve(value) {
console.log(value);
// resolve被调用时修改状态
that.state = RESOLVED;
}
function reject(value) {
console.log(value);
// reject被调用时修改状态
that.state = REJECTED;
}
fn(resolve, reject);
}
复制代码
OK,如今已经能知道Promise当前所处的状态了,可是任务完了得拿到结果吧,MyPromise
的 resolve
被调用,那也只是MyPromise
知道任务完成了,用户还不知道呢。
因此咱们须要回调函数告诉用户,是的,其实就是回调函数。
这时候就轮到 then
方法出场了,用户经过then
方法传入回调函数, MyPromise
将在成功调用 resolve
时调用用户传入的回调函数。
开始改造代码,MyPromise
内部须要变量存储回调函数,then
方法的做用就是将用户传入的回调函数赋予 MyPromise
内的变量。
因此 then
方法长这样,接收两个参数,一个是成功时的回调函数,一个是失败时的回调函数。
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function MyPromise(fn) {
const that = this;
that.state = PENDING;
// 两个存储回调函数的变量
that.resolvedCallback;
that.rejectedCallback;
function resolve(value) {
console.log(value);
that.state = RESOLVED;
// 调用用户的回调函数,并告知结果
that.resolvedCallback && that.resolvedCallback(value);
}
function reject(value) {
console.log(value);
that.state = REJECTED;
// 调用用户的回调函数,并告知结果
that.rejectedCallback && that.rejectedCallback(value);
}
fn(resolve, reject);
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
that.resolvedCallback = onFulfilled;
that.rejectedCallback = onRejected;
}
复制代码
是的,一个简版Promise几乎大功告成,让咱们再试试在浏览器执行以下代码(注意我删除了 resolve
和 reject
里的console语句)
(function () {
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function MyPromise(fn) {
const that = this;
that.state = PENDING;
// 两个存储回调函数的变量
that.resolvedCallback;
that.rejectedCallback;
function resolve(value) {
that.state = RESOLVED;
// 调用用户的回调函数,并告知结果
that.resolvedCallback && that.resolvedCallback(value);
}
function reject(value) {
that.state = REJECTED;
// 调用用户的回调函数,并告知结果
that.rejectedCallback && that.rejectedCallback(value);
}
fn(resolve, reject);
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
that.resolvedCallback = onFulfilled;
that.rejectedCallback = onRejected;
}
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('你又看到我啦~');
}, 2000);
}).then(value => {
console.log(value);
});
})();
复制代码
上面的代码,用法上已经和Promise长得差很少了,可是若是咱们屡次调用 then
方法呢?
是的,只有最后一个 then
方法里的回调函数能执行,这固然无法知足咱们的须要。
因而,将两个回调函数改为函数数组(请回想一下前置知识),并在状态更改时遍历调用回调函数。
改造后的代码以下:
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function MyPromise(fn) {
const that = this;
that.state = PENDING;
// 注意这里变成了两个数组
that.resolvedCallbackArray = [];
that.rejectedCallbackArray = [];
function resolve(value) {
that.state = RESOLVED;
// 遍历用户的回调函数,告知结果
that.resolvedCallbackArray.forEach(cbFn => cbFn(value));
}
function reject(value) {
that.state = REJECTED;
// 遍历用户的回调函数,告知结果
that.rejectedCallbackArray.forEach(cbFn => cbFn(value));
}
fn(resolve, reject);
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
that.resolvedCallbackArray.push(onFulfilled);
that.rejectedCallbackArray.push(onRejected);
// 这里是为了链式调用,即myPromise.then().then().then()...
return that;
}
复制代码
如今咱们能够屡次调用 then
方法了。试试在控制台运行下面的代码:
(function () {
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function MyPromise(fn) {
const that = this;
that.state = PENDING;
// 注意这里变成了两个数组
that.resolvedCallbackArray = [];
that.rejectedCallbackArray = [];
function resolve(value) {
that.state = RESOLVED;
// 遍历用户的回调函数,告知结果
that.resolvedCallbackArray.forEach(cbFn => cbFn(value));
}
function reject(value) {
that.state = REJECTED;
// 遍历用户的回调函数,告知结果
that.rejectedCallbackArray.forEach(cbFn => cbFn(value));
}
fn(resolve, reject);
}
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this;
that.resolvedCallbackArray.push(onFulfilled);
that.rejectedCallbackArray.push(onRejected);
return that;
}
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('你又看到我啦~');
}, 2000)
}).then(value => {
console.log('第一次', value);
}).then(value => {
console.log('第二次', value);
});
})();
复制代码
上面已是简版Promise的实现了。
可是咱们还能够更完善一点,加强 MyPromise
的健壮性。
例如,若用户自定义函数在执行过程当中发生了错误,会中断程序的执行,因而咱们增长try...catch...
语句,并在发生错误时主动执行reject
函数告知用户。
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
复制代码
又或者,对参数进行校验,状态进行判断等,以 then
为例,若用户传入的参数不是函数呢? 或者Promise的状态已经时rejected
或resolved
,此时调用then
呢?
改造 then
后代码以下:
MyPromise.prototype.then = function(onFulfilled, onRejected) {
if(typeof onRejected !== 'function') {
onRejected = v => v;
}
if(typeof onFulfilled !== 'function') {
onFulfilled = v => { throw r };
}
const that = this;
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)
}
}
复制代码
还有其余方法在理解了基本原理以后能够本身根据规范改造,本文到此告一段落,感谢阅读。