保姆级教学手写Promise,内容较多,但是详细。源码放在最后。
在index.html中编写如下代码
let p = new Promise((resolve, reject) => {
resolve("执行成功");
})
p.then(result => {
console.log(result);
}, reason=>{
console.log(reason);
})
创建一个promise.js文件在index.html中引入
1. Promise能够通过new的方式调用,所以Promise是构造函数的形式。
2. 构造Promise实例的时候传过去了一个函数,使用executor(执行器函数接收这个形参)。
3. Promise实例能够调用then方法,所以在构造函数的原型上要添加then方法。
4. then方法接收两个函数。
// Promsie构造函数
function Promise(executor) {}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {}
1. executor函数是同步调用的,所以Promise实例一构造就会调用executor函数。
2. executor函数中有两个参数,分别是resolve函数和reject函数。
3. resolve和reject函数中接收一个参数用于存储数据。
// Promsie构造函数
function Promise(executor) {
// resolve函数
function resolve(data) {}
// reject函数
function reject(data) {}
// 执行器函数是同步调用的,立即执行
executor(resolve, reject);
}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {}
1. Promise对象有两个属性,一个是PromiseState用于存储状态码,一个是PromiseResult用于存储返回结果。
2. resolve和reject函数调用后会分别修改Promise实例对象的状态码为fulfilled和rejected。
3. resolve和reject函数会修改Promise实例对象的结果。
// Promsie构造函数
function Promise(executor) {
// 添加属性
this.PromiseState = 'pending'; // 默认状态为pending
this.PromiseResult = null; // 默认值为空
// resolve函数
function resolve(data) {
// 1. 修改Promise实例对象的状态(PromiseState)
this.PromiseState = 'fulfilled';
// 2. 设置对象结果值(PromiseResult)
this.PromiseResult = data;
}
// reject函数
function reject(data) {}
// 执行器函数是同步调用的,立即执行
executor(resolve, reject); // 调用执行器函数
}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {}
打印Promise实例对象我们会发现状态码和存储值并未改变
let p = new Promise((resolve, reject) => {
resolve("执行成功");
})
console.log(p);
如果我们打印一下resolve里面的this和Promise对象里的this可以发现问题,resolve函数里面的this指向的是window。
这个时候我们需要将resolve函数和reject函数里面的this指向Promise实例对象。
// Promsie构造函数
function Promise(executor) {
// 添加属性
this.PromiseState = 'pending'; // 默认状态为pending
this.PromiseResult = null; // 默认值为空
const _this = this;
// resolve函数
function resolve(data) {
// 1. 修改Promise实例对象的状态(PromiseState)
_this.PromiseState = 'fulfilled';
// 2. 设置对象结果值(PromiseResult)
_this.PromiseResult = data;
}
// reject函数
function reject(data) {
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
}
// 执行器函数是同步调用的,立即执行
executor(resolve, reject); // 调用执行器函数
}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {}
问题就解决了, 不管是调用resolve还是reject都能成功显示结果
1. 在Promise对象中抛出错误会使Promise实例对象的状态码变为rejected。
2. 使用try-catch可以捕获到错误。
3. 抛出的错误信息将会变成Promise实例对象的存储数据。
let p = new Promise((resolve, reject) => {
// resolve("执行成功");
// reject("执行失败");
// 抛出异常
throw new Error("异常");
})
console.log(p);
我们在executor函数执行的时候使用try-cahtch检测是否抛错,抛错就调用reject函数改变Promise实例对象的状态和存储错误信息。
// Promsie构造函数
function Promise(executor) {
// 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
const _this = this;
// resolve函数
function resolve(data) {
_this.PromiseState = 'fulfilled';
_this.PromiseResult = data;
}
// reject函数
function reject(data) {
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
}
try {
// 执行器函数是同步调用的,立即执行
executor(resolve, reject); // 调用执行器函数
} catch (e) {
reject(e);
}
}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {}
如下代码在正常的Promise中执行只会接收第一条resolve("执行成功")的结果,也就是PromiseState为"fulfilled",PromiseResult为"执行成功"。
let p = new Promise((resolve, reject) => {
resolve("执行成功");
reject("执行失败");
// throw new Error("异常");
})
console.log(p);
但是我们写的代码第二次执行reject会覆盖掉上一次的PromiseState和PromiseResult
所以需要在每次执行resolve函数和reject函数时要判断状态是否改变,如果状态已经改变过了就不能再次改变状态了。
// Promsie构造函数
function Promise(executor) {
// 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
const _this = this;
// resolve函数
function resolve(data) {
// 判断状态
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'fulfilled'; // resolved
_this.PromiseResult = data;
}
// reject函数
function reject(data) {
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
}
try {
// 执行器函数
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {}
我们再看刚才的运行结果就成功了。
1. 在then方法中Promise状态为fulfilled执行第一个函数,rejected执行第二个函数。
2. 在执行两个回调函数的时候传入了一个参数result(另外一个参数为reason)。
3. 参入的参数为Promise实例对象的PromiseResult,如打印的result为"执行成功"。
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
// 根据PromiseState判断调用哪个回调函数
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult);
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
}
如果我们在Promise对象 中调用的是异步回调函数,那么在Promise对象的状态还未改变(pending)时就执行了then,但是在正常的Promise中会等异步执行完毕后再调用then的值。
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve("执行成功");
}, 1000);
})
console.log(p);
p.then(result => {
console.log(result);
}, reason=>{
console.log(reason);
})
我们怎么解决这个问题?
1. 怎么判断是不是异步执行?
如果then方法中判断Promise对象的状态为pending,那么Promise中则进行的是异步操作。
2. 什么时候执行the中的回调呢?
当resolve函数被调用的时候,也就是上面代码1秒过后就可以改变Promise的状态和值并执行then中的回调函数。
3. resolve怎么能够知道then中的回调函数并调用呢?
在then中判断状态为pending时使用Promise中的属性将回调函数装起来。
4. 现在已经有了then中的回调函数,resolve怎么知道什么时候需要执行then中的回调函数?
判断Promise对象是否存在回调函数,只有异步执行的时候才会在then中将回调函数保存起来。
// Promsie构造函数
function Promise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
// 声明属性,用于保存回调函数
this.callback = {};
const _this = this;
// resolve函数
function resolve(data) {
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'fulfilled';
_this.PromiseResult = data;
// 调用成功的回调函数
if (_this.callback.onResolved) {
_this.callback.onResolved(data);
}
}
// reject函数
function reject(data) {
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
if (_this.callback.onReject) {
_this.callback.onReject(data);
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult);
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
// 如果判断为pending状态,说明Promise中执行的是异步任务
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callback = {
onResolved:onResolved,
onRejected:onRejected
}
}
}
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("执行成功");
}, 1000);
})
p.then(result => {
console.log(1, result);
}, reason => {
console.log(1, reason);
})
p.then(result => {
console.log(2, result);
}, reason => {
console.log(2, reason);
})
以上代码在正常的Promise中执行的结果是
而我们的代码运行的结果是
为什么会出现这样的问题呢?
1. 进入第一个then方法时,Promise对象状态为pending,保存callback.onResult为console.log(1, result) 。
2. 在第一个then执行完毕后,执行第二个then,再次调用then方法。
3. 这时候Promise对象状态依然为pending,覆盖callback.onResult为console.log(2, result) 。
所以只会输出第二个then的结果而不会输出第一个then的结果。
怎么解决这个问题呢?
1. 我们将所有的回调函数都装起来,这时候callback就要使用数组而不能使用对象。
2. 每次使用将then中pending状态的回调函数添加进数组中。
3. 在每次调用resolve函数过后,通过遍历的方式调用所有的callback函数。
// Promsie构造函数
function Promise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
// 声明属性,保存回调函数
this.callbacks = [];
const _this = this;
// resolve函数
function resolve(data) {
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'fulfilled'; // resolved
_this.PromiseResult = data;
// 遍历执行回调函数
_this.callbacks.forEach(item => {
item.onResolved(data);
})
}
// reject函数
function reject(data) {
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
_this.callbacks.forEach(item => {
item.onRejected(data);
})
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult);
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
// 如果判断为pending状态,说明Promise中执行的是异步任务
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: onResolved,
onRejected: onRejected
})
}
}
当我们再次运行,执行结果就正确了
let p = new Promise((resolve, reject) => {
resolve("执行成功");
})
const res = p.then(result => {
console.log(result);
}, reason => {
console.log(reason);
})
console.log(res);
以上代码我们使用正常的Promise执行then会返回一个新的Promise对象,因为then中没有设置返回值return,所以返回值为undefined。
但是我们写的Promise调用then方法的时候并不会有返回值return,所以打印出来的res为undefined。
如何解决这个问题呢?
1. 在then方法中返回一个Promise对象。
2. 根据上一个Promise对象实例不同的状态确定返回的Promise是调用的是resolve还是reject来改变自己的状态和返回值。
3. resolve和reject中的参数就是回调函数的返回值return(如果回调函数没有返回值就为undefined)。
Promise.prototype.then = function (onResolved, onRejected) {
return new Promise((resolve, reject) => {
if (this.PromiseState === 'fulfilled') {
// 获取回调函数的执行结果
let result = onResolved(this.PromiseResult);
// 改变返回Promise的状态和结果值
resolve(result);
}
if (this.PromiseState === 'rejected') {
let result = onRejected(this.PromiseResult);
}
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: onResolved,
onRejected: onRejected
})
}
})
}
当我们再次执行的时候就能得到then方法返回的一个正确的Promise结果
但是如果我们在then函数中返回的是一个Promise对象呢?
let p = new Promise((resolve, reject) => {
resolve("执行成功");
})
const res = p.then(result => {
console.log(result);
return new Promise((resolve, reject) => {
resolve("success");
})
}, reason => {
console.log(reason);
})
console.log(res);
正常的Promise会返回如下结果,返回的Promise对象的状态个和结果值覆盖先前的Promise状态和结果值。
而我们的Promise会得到如下的结果,返回的Promise对象被变成了结果值保存起来。
我们又怎么解决这个问题呢?
1. 我们需要判断回调函数的返回结果是不是一个Promise对象。
2. 如果是一个Promise对象就可以调用then方法,根据Promise对象中的状态和返回值判断调用哪一个then回调函数。
3. 第一个回调中执行resolve,第二个回调中执行reject。
Promise.prototype.then = function (onResolved, onRejected) {
return new Promise((resolve, reject) => {
if (this.PromiseState === 'fulfilled') {
// 获取回调函数的执行结果
let result = onResolved(this.PromiseResult);
if (result instanceof Promise) {
// 如果是 Promise 类型的对象
result.then(res => {
resolve(res);
}, reason => {
reject(reason);
})
} else {
// 结果的对象状态为成功
resolve(result);
}
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: onResolved,
onRejected: onRejected
})
}
})
}
看一下效果非常成功
如果我们在Promise对象中使用异步调用会出现什么结果
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("执行成功");
}, 1000);
})
const res = p.then(result => {
return "then中返回的Promise";
}, reason => {
console.log(reason);
})
console.log(res);
你可能会疑惑上一次异步调用(七、异步任务回调的执行)中then都能正常操作,为什么这一次就会出问题。
分析一下:
1. Promise对象中进行的时异步调用,要1秒钟后才会执行resolve函数去改变Promise对象的状态和结果值。
2. 到达then函数的时候Promise状态值还是为pending。
3. then函数现在返回的是一个Promise对象,而在状态为pending的时候并没有对返回的Promise对象做任何改变Promise对象的操作,所以状态依旧为pending,结果值为null。
4. 注意这里的输出"执行成功"是p这个初始的Promise对象的resolve函数调用then执行的,和返回的新Promise对象没有关系。
解决方法:
1. 在异步调用情况下,then函数保存回调函数的同时设置返回Promise的状态和返回值。
2. 返回的Promise对象的结果值是then中回调函数的返回值(return),这个时候回调函数就需要调用获取返回值。
// 如果判断为pending状态,说明Promise中执行的是异步任务
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function () {
let result = onResolved(this.PromiseResult);
resolve(result);
},
onRejected: onRejected
})
}
这个时候返回的Promise就正确了
接下来考虑then中返回的是Promise对象的情况(这部分就可以当做同步来处理)
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("执行成功");
}, 1000);
})
const res = p.then(result => {
// return "then中返回的Promise"
return new Promise((resolve, reject)=>{
resolve("then中的Promise")
})
}, reason => {
console.log(reason);
})
console.log(res);
// 如果判断为pending状态,说明Promise中执行的是异步任务
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function () {
let result = onResolved(this.PromiseResult);
if (result instanceof Promise) {
result.then(res => {
resolve(res)
}, reason => {
reject(reason)
})
} else {
resolve(result);
}
},
onRejected: onRejected
})
}
运行结果是没有问题的
但是我们在then中抛出错误还是会出错,所以我们要在回调函数中使用try-chatch监测错误
// 如果判断为pending状态,说明Promise中执行的是异步任务
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: function () {
try {
let result = onResolved(this.PromiseResult);
if (result instanceof Promise) {
result.then(res => {
resolve(res)
}, reason => {
reject(reason)
})
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
},
onRejected: onRejected
})
}
我们刚才一直在为then的第一个回调进行操作,接下来对第二个回调进行补全。以下是then函数完整的代码。
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
return new Promise((resolve, reject) => {
if (this.PromiseState === 'fulfilled') {
let result = onResolved(this.PromiseResult);
if (result instanceof Promise) {
// 如果是 Promise 类型的对象
result.then(res => {
resolve(res);
}, reason => {
reject(reason);
})
} else {
// 结果的对象状态为成功
resolve(result);
}
}
if (this.PromiseState === 'rejected') {
let result = onRejected(this.PromiseResult);
if (result instanceof Promise) {
result.then(res => {
resolve(res);
}, reason => {
reject(res);
})
} else {
reject(result);
}
}
// 如果判断为pending状态,说明Promise中执行的是异步任务
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function () {
try {
let result = onResolved(this.PromiseResult);
if (result instanceof Promise) {
result.then(res => {
resolve(res)
}, reason => {
reject(reason)
})
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
},
onRejected: onRejected
})
}
})
}
但是我们会发现在每一个状态中都有重复的操作——判断返回值是否为Promise,并为Promise设置状态和返回值。
这个时候我们就要封装一个函数,方便我们代码的后期可维护性。
1. 在返回的Promise对象中封装callback函数,直接从下面复制一个结构放在函数中即可。
2. 传入一个参数用于定义调用onResolved函数还是onRejected函数。
// 封装函数
function callback() {
try {
// 获取回调函数的执行结果
let result = onResolved(this.PromiseResult);
if (result instanceof Promise) {
// 如果是 Promise 类型的对象
result.then(res => {
resolve(res);
}, reason => {
reject(reason);
})
} else {
// 结果的对象状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
但是我们注意,这里的this在一个函数中,指向的是window,我们要让它指向Promise实例对象才能用到正确的PromiseResult。这里不单独演示,会在下一次代码中做修改。
let p = new Promise((resolve, reject) => {
resolve("执行成功");
console.log(111);
})
const res = p.then(result => {
console.log(222);
}, reason => {
console.log(reason);
})
console.log(333);
以上代码在正常的Promise中执行结果如下
我们的代码运行是这样
出现问题的原因是then方法中的代码执行是异步的,而Promise和全局下的代码是同步的(不了解同步和异步可以去该专栏下看相关文章),所以先输出111和333,最后输出222。
解决问题的方法很简单,我们只需让then中的回调函数变为异步执行即可。
我们这里用到的是微任务队列函数,一共有两种情况要加上该函数。
1. Promise对象中为同步执行时,then中的回调函数直接执行。
2. Promise对象中为异步执行时,then中的回调函数在resolve和reject调用后执行。
所以我们现在就把Promise的构造函数和Promise的then方法写写完了,以下是完整代码。
// Promsie构造函数
function Promise(executor) {
// 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 声明属性,保存回调函数
this.callbacks = [];
const _this = this;
// resolve函数
function resolve(data) {
// 判断状态
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'fulfilled';
_this.PromiseResult = data;
// 执行回调函数
queueMicrotask(() => {
_this.callbacks.forEach(item => {
item.onResolved(data);
})
})
}
// reject函数
function reject(data) {
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
queueMicrotask(() => {
_this.callbacks.forEach(item => {
item.onRejected(data);
})
})
}
try {
// 执行器函数是同步调用的,立即执行
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
const _this = this;
return new Promise((resolve, reject) => {
// 封装函数
function callback(type) {
try {
let result = type(_this.PromiseResult);
if (result instanceof Promise) {
result.then(res => {
resolve(res);
}, reason => {
reject(reason);
})
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
// 根据PromiseState判断调用哪个回调函数
if (this.PromiseState === 'fulfilled') {
queueMicrotask(() => {
callback(onResolved)
});
}
if (this.PromiseState === 'rejected') {
queueMicrotask(() => {
callback(onRejected)
});
}
// 如果判断为pending状态,说明Promise中执行的是异步任务
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
}
})
}
})
}