7. 手写 Promise 学习笔记
朋友们,大家好!本期内容是《你应该掌握的 JavaScript 高阶技能》的第七期,主要内容:
Promise
手写实现,如果您还没有学习过Promise
,可以先看此文章 Promise 与 异步编程 学习笔记,最后,望本文能对您有所帮助!☀️
本期内容参考:
⇒ 尚硅谷 Web 前端 Promise 教程从入门到精通
特别鸣谢❤️
目录
- 7. 手写 Promise 学习笔记
- 7.1 初始结构搭建
- 7.2 resolve 与 reject 结构搭建
- 7.3 resolve 与 reject 代码实现
- 7.4 throw 抛出异常改变状态
- 7.5 Promise 对象只能修改一次
- 7.6 then 方法执行回调
- 7.7 异步任务回调的执行
- 7.8 指定多个回调的实现
- 7.9 同步修改状态 then 方法结果返回
- 7.10 异步任务修改 then 方法
- 7.11 then 方法完善与优化
- 7.12 catch 方法
- 7.13 resolve 方法封装
- 7.14 reject 方法封装
- 7.15 all 方法封装
- 7.16 race 方法封装
- 7.17 then 方法回调的异步执行
- 7.18 class 版本
7.1 初始结构搭建
//声明构造函数
function Promise(executor/*执行器函数*/) {
}
// Promise.then()
// 根据 Promise 对象状态执行相应的回调函数
// 添加 then 方法
Promise.prototype.then = function() {
// then 方法接收两个参数
// 一个成功的回调函数 一个失败的回调函数
}
7.2 resolve 与 reject 结构搭建
//声明构造函数
function Promise(executor) {
// resolve 函数
function resolve(data) { }
// reject 函数
function reject(data) { }
// 执行器函数 且是同步调用
executor(resolve,reject) { }
}
// then 方法
Promise.prototype.then = function(onResolved/*成功回调*/,onRejected/*失败回调*/) {
}
7.3 resolve 与 reject 代码实现
// 调用 resolve 和 reject 会更改 Promise 对象状态
//声明构造函数
function Promise(executor) {
// 添加相应的属性
this.PromiseState = "pending";
this.PromiseResult = null;
// resolve 函数
function resolve(data) {
// console.log(this); // window
// 修改对象的状态 pending --> fulfilled
self.PromiseState = "fulfilled";
// 设置对象结果值
self.PromiseResult = data;
}
// reject 函数
function reject(data) {
// 实现方法跟 resolve 类似
// 修改对象的状态 pending --> rejected
self.PromiseState = "rejected";
self.PromiseResult = data;
}
// 执行器函数 且是同步调用
executor(resolve,reject) { }
}
7.4 throw 抛出异常改变状态
- 如果
executor
执行器中抛出异常,此时我们的Promise
对象状态不会发生改变。
try {
executor(resolve,reject);
} catch(e) {
// 修改 promise 对象状态设置为失败
reject(e);
}
7.5 Promise 对象只能修改一次
// todo...
function resolve(data) {
// 判断状态是否已经改变
if(self.PromiseState !== 'pending') {
return;
}
// todo...
}
// reject 函数同理!
// todo...
7.6 then 方法执行回调
// then 方法
Promise.prototype.then = function (onResolved,onRejected) {
if(this.PromiseState === 'fulfilled') {
//调用成功的回调函数
onResolved(this.PromiseResult);
}
if(this.PromiseState === 'rejected') {
//调用失败的回调函数
onRejected(this.PromiseResult);
}
}
7.7 异步任务回调的执行
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok');
}, 1000);
});
p.then((value) => {
console.log(value);
},(reason) => {
console.log(reason);
})
-
此时我们写的
Promise
,执行后是没有任何输出。 -
思路:当执行器函数
executor
内部是一个异步任务的时候,则先指定的回调函数p.then(成功回调,失败回调)
(我们先保存),等到执行器函数执行完毕也就是改变状态之后,再执行指定的回调函数。
function Promise(executor) {
// todo...
this.callbacks = [];
// 异步任务所保存的回调函数数组
// todo...
}
- 保存成功的回调
// 状态未改变 --> 就保存回调函数
Promise.prototype.then = function(onResolved,onRejected) {
// todo...
if(this.PromiseState === 'pending') {
// 表明异步任务
// 保存相应的回调函数
this.callbacks = {
onResolved,
onRejected,
}
}
// todo...
}
- 状态改变,执行相应的回调函数
function Promise(executor) {
// todo...
function resolve(data) {
if(self.PromiseState !== 'pending') {
return;
}
self.PromiseState = "fulfilled";
self.PromiseResult = data;
// 状态变为 fulfilled 我们要执行成功的回调函数
if(self.callbacks.onResolved) {
self.callbacks.onResolved(data);
}
}
function reject(data) {
if(self.PromiseState !== 'pending') {
return;
}
self.PromiseState = "rejected";
self.PromiseResult = data;
// 状态变为 reject 我们要执行失败的回调函数
if(self.callbacks.onRejected) {
self.callbacks.onRejected(data);
}
}
// todo...
}
- 这样在 1 秒后就可以在控制台输出 “ok”。
7.8 指定多个回调的实现
- 如果我们指定多个回调函数,此时只会执行我们最后一次指定的回调函数,也就是说前面指定的回调函数都被覆盖了。
// 第一个调用 then 方法
result.then((value) => {
console.log(value);
}, (reason) => {
console.log(reason);
});
// 第二次调用 then 方法
result.then((value) => {
alert(value);
}, (reason) => {
alert(reason);
});
// 结果只有弹窗内容,控制台没有输出
- 覆盖的原因:此前保存回调函数的方式:直接赋值。
// todo...
this.callbacks = {
onResolved,
onRejected,
}
// todo...
- 改善:将回调函数加入数组中
Promise.prototype.then = function (onResolved,onRejected) {
// todo...
if(this.PromiseState === 'pending') {
this.callbacks.push({
onResolved,
onRejected,
})
}
// todo...
}
- 此时
callbacks
数组中每一个数组元素都是一个包含成功与失败的回调函数的对象 - 当
Promise
对象状态改变为fulfilled
时,将同步调用callbacks
中成功的回调函数。rejected
同理!
function Promise(executor) {
// todo...
const self = this;
function resolve(data) {
// todo...
// 调用成功的回调函数
/*
原:
if(self.callbacks.onResolved) {
self.callbacks.onResolved(data);
}
*/
self.callbacks.forEach((item) => {
item.onResolved(data);
});
}
function reject(data) {
// todo...
// 调用失败的回调函数
self.callbacks.forEach((item) => {
item.onRejected(data);
});
}
}
7.9 同步修改状态 then 方法结果返回
- 回顾:
then
方法返回结果是一个Promise
对象,其状态取决于指定的回调函数内部执行的结果
Promise.prototype.then = function(onResolved, onRejected) {
// 1. 返回 Promise 对象
return new Promise((resolve,reject) => {
// todo...
});
}
Promise.prototype.then = function(onResolved, onRejected) {
// 1. 返回 Promise 对象
return new Promise((resolve,reject) => {
if(this.PromiseState === 'fulfilled') {
// 执行成功的回调
try {
let result = onResolved(this.PromiseResult);
// 3. 判断result 返回值是否是 Promise 对象
if(result instanceof Promise) {
// 此 promise 的结果就会成为 then 方法返回的结果
// 调用 then 方法为其指定成功与失败的回调函数
result.then(v => {
// 内部 fulfilled 返回 fulfilled
resolve(v);
},r => {
// 内部 rejected 返回 rejected
reject(r);
});
} else {
// 4. 结果的对象状态为成功
resolve(result);
}
} catch(e) {
// 2. 抛出异常
// 结果的对象状态为失败
reject(e);
}
}
// todo...
});
}
7.10 异步任务修改 then 方法
- 此时如果执行器函数内部是一个异步任务,
Promise
对象调用then
方法返回的结果是pending
- 这是因为调用
then
方法时,此时的状态为pending
,仅是保存了相应的回调函数,没有对then
返回结果进行处理。 - 所以这里不仅仅要保存相应的回调函数,也要对
then
返回结果进行处理!
Promise.prototype.then = function (onResolved, onRejected) {
const self = this;
// todo...
return new Promise((resolve,reject) => {
if(this.PromiseState === 'pending') {
this.callbacks.push({
// 成功的回调函数并改变 Promise.then() 方法返回结果对象的状态
onResolved: function() {
try {
let result = onResolved(self.PromiseResult);
// 判断 result 是否为 Promise 对象
//处理返回结果
if(result instanceof Promise) {
result.then(v => {
// 内部 fulfilled 返回 fulfilled
resolve(v);
},r => {
// 内部 rejected 返回 rejected
reject(r);
});
} else {
resolve(result);
}
} catch(e) {
reject(e);
}
},
// 成功的回调函数
onRejected: function() {
// onRejected 是跟 onResolved 一样的!只是 result = onRejected(this)
}
})
}
})
}
7.11 then 方法完善与优化
- 我们发现
onResolved
与onRejected
回调函数内容基本一致,优化代码,使其封装函数callback
Promise.prototype.then = function (onResolved, onRejected) {
const self = this;
function callback(type/*action*/) {
try {
// 对应 let result = onResolved(self.PromiseResult);
// let result = onRejected(self.PromiseResult);
let result = type(self.PromiseResult);
if (result instanceof Promise) {
//如果是 promise 对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch(e) {
reject(e);
}
}
if(this.PromiseState === 'fulfilled') {
callback(onResolved);
}
if(this.PromiseState === 'rejected') {
callback(onRejected);
}
if(this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: function() {
callback(onResolved);
},
onRejected: function() {
callback(onRejected);
}
})
}
}
- 这样
then
基本完成!
7.12 catch 方法
catch
方法本质上就是then
的语法糖,指定失败的回调函数并执行。
Promise.prototype.catch = function(onRejected) {
return this.then(undefined,onRejected);
}
- 处理链式调用
then
问题 - 异常穿透和值传递
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('ok');
});
});
p.then(value => {
return new Promise((resolve, reject) => {
resolve('success');
});
}).then((value) => {
console.log(value); // success
}).then(value => {
console.log(value); // undefined
});
// 此时,我们为 then 方法只传递了成功的回调函数,失败的回调函数 onRejected 为 undefined,会抛出 TypeError: type in not a function at callback
// 因为我们没有指定失败回调,对于这样情况,我们要默认传入参数
Promise.prototype.then = function (onResolved, onRejected) {
// 判断回调函数参数
if(typeof onRejected !== 'function') {
onRejected = (reason) => {
throw reason;
}
}
if(typeof onResolved !== 'function') {
onResolved = value => value;
}
}
7.13 resolve 方法封装
// Promise.resolve 类方法
Promise.resolve = function(value) {
return new Promise((resolve,reject) => {
if(value instanceof Promise) {
value.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
resolve(value);
}
})
}
7.14 reject 方法封装
Promise.reject = function (reason) {
return new Promise((undefined, reject) => {
reject(reason);
});
}
7.15 all 方法封装
Promise.all = function(promises) {
return new Promise((resolve,reject) => {
let count = 0;
// 成功的 PromiseResult
let arr = []
// 遍历 promises 数组
for(let i = 0; i < promises.length; i++) {
promises[i].then(v => {
// 每个 promise 对象都成功
// 将当前 promise 对象存入数组
// 使用 arr.push(v) 有问题,要考虑异步任务问题!
count++;
arr[i] = v;
if(count === promises.length) {
resolve(arr);
}
}, r => {
reject(r);
})
}
})
}
7.16 race 方法封装
- 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态
Promise.race = function(promises) {
return new Promise((resolve,reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v);
}, r => {
reject(r);
})
}
});
}
7.17 then 方法回调的异步执行
let p = new Promise((resolve,reject) => {
resolve('ok');
console.log('1');
})
p.then(value => {
console.log(value);
console.log('3');
});
console.log('2');
// 输出结果为 1 2 ok 3
// 说明 then 方法是异步任务 内部是同步任务
- 简单方法:使用
setTimeout
包装,但是这样是有问题的,这里仅仅是模拟。
function Promise(executor) {
// todo...
function resolve(data) {
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(data);
})
});
}
function reject(data) {
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(data);
})
});
}
// todo...
}
Promise.prototype.then = function (onResolved, onRejected) {
// todo...
if (this.PromiseState === 'fulfilled') {
// 异步执行
setTimeout(() => {
callback(onResolved);
})
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected);
})
}
// todo...
}
7.18 class 版本
//class 类
class Promise {
//构造方法
constructor(executor) {
//添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callbacks = [];
//保存实例对象的 this 值
const self = this;
//resolve 函数
function resolve(data) {
if (self.PromiseState !== 'pending') {
return;
}
//console.log(this);//window
//1.修改对象改变状态(promiseState)
self.PromiseState = 'fulfilled';//resolved
//2.设置对象结果值(promiseResult)
self.PromiseResult = data;
//调用成功的回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(data);
})
})
}
//reject 函数
function reject(data) {
//判断状态
if (self.PromiseState !== 'pending') {
return;
}
//1.修改对象改变状态(promiseState)
self.PromiseState = 'rejected';//rejected
//2.设置对象结果值(promiseResult)
self.PromiseResult = data;
//调用失败的回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(data);
})
})
}
// executor 执行器函数是同步调用的
try {
executor(resolve, reject);
} catch (e) {
//修改 promise 对象状态设置为【失败】
reject(e);
}
}
//then 方法
then(onResolved, onRejected) {
const self = this;
//判断回调函数参数
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason;
}
}
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new Promise((resolve, reject) => {
//封装函数
function callback(type) {
try {
//获取回调函数的执行结果
//console.log(this.PromiseResult);
//console.log(onResolved);
let result = type(self.PromiseResult);
//console.log(result);
if (result instanceof Promise) {
//如果是 promise 对象
result.then(v => {
resolve(v);
}, r => {
reject(r);
}
)
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
//调用回调函数 PromiseState
// this 指向调用者 即创建的实例
if (this.PromiseState === 'fulfilled') {
//异步执行
setTimeout(() => {
callback(onResolved);
})
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected);
})
}
//判断 pending 状态
if (this.PromiseState === 'pending') {
//保存回调函数
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
}
})
}
})
}
//catch 方法
catch(onRejected) {
return this.then(undefined, onRejected);
}
//resolve 方法
//类方法 静态方法 不属于实例对象的方法
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)
}, r => {
reject(r);
})
} else {
resolve(value);
}
})
}
//reject 方法
static reject(reason) {
return new Promise((undefined, reject) => {
reject(reason);
})
}
//all 方法
static all(promises) {
return new Promise((resolve, reject) => {
//遍历
let count = 0;
let arr = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
//每个 promise 对象都成功
count++;
//将当前 promise 对象存入数组
// arr.push(v);//顺序有问题
arr[i] = v;
if (count === promises.length) {
resolve(arr);
}
}, r => {
reject(r);
})
}
})
}
//race 方法
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v);
}, r => {
reject(r);
})
}
})
}
}
第七期学习内容就这么多啦,如果您觉得内容不错的话,望您能关注🤞点赞👍收藏❤️一键三连!