我们从最简单的切入口开始。
1.整体结构的搭建
要实现的官方的功能
let p = new Promise((resolve,reject)=>{
resolve("ok");
});
p.then(value=>{
console.log(value);
},reason => {
console.log(reason)
});
观察上面的结构:
-
Promise
构造函数有一个回调函数 我们就给起名字为executor
- 实例对象上面有个
then
方法 -
then
方法有两个回调函数onResolved
,onRejected
为了和Promise
做区分,这里起名字为PromiseA
目标就明朗了,我们的实现版本如下:
function PromiseA(executor){
}
PromiseA.prototype.then = function (onResolved,onRejected){
}
测试用例:
let p1 = new PromiseA((resolve,reject)=>{
resolve("ok");
});
p1.then(value=>{
console.log(value);
},reason => {
console.log(reason)
});
//没有报错,说明实现了我们最初的目的
2.resolve
和reject
的搭建
let p = new Promise((resolve,reject)=>{
resolve("ok");
});
p.then(value=>{
console.log(value);
},reason => {
console.log(reason)
});
2.1 还是观察官方的例子,发现构造函数中的回调函数中
构造函数中的是同步调用的 怎么同步调用呢? 我们直接在构造函数里面把回调函数exector
调用一下就可以
function PromiseA(executor){
//同步调用一下
executor();
}
2.2 同步调用以后发现,回调函数里面还有两个回调的参数.resolve
和reject
.那么这两个参数是哪里来的呢?他们是什么?
通过观察发现 resolve("ok")
.原来它们都是函数,同时这个函数里面还有 调用的时候传入的参数.好,安排.
function PromiseA(executor){
function resolve(data){
}
function reject(data){
}
//同步调用一下
executor(resolve,reject);
}
3.resolve
和reject
的实现
在之前的研究中我们知道resolve("OK")
被调用的时候,它会有以下两个作用:
- 修改实例对象Promise的状态为
fulfilled
. - 它传入的参数
ok
就是结果值
所以我们需要实现这两步:
- 修改状态---那么就需要一个初始状态
- 改变结果值---那么就需要一个存储结果值的参数
我们看一下官网Promise
的内部状态
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "OK"
可以看到它内部有两个内部属性:PromiseState
和PromiseResult
.那我们就知道了怎么做了.
function PromiseA(executor){
//添加属性
this.PromiseState = "pending";
this.PromiseResult = null;
//resolve函数
function resolve(data){
//1.修改对象的状态(PromiseState)
this.PromiseState = "fulfilled";
//2.设置对象的结果值(PromiseResult)
this.PromiseResult = data;
}
function reject(data){
}
//同步调用一下
executor(resolve,reject);
}
这里有一个问题,我们调用上面的实例会发现:
let p1 = new PromiseA((resolve,reject)=>{
resolve("ok");
});
console.log(p1)
// PromiseA { PromiseState: 'pending', PromiseResult: null }
我们的修改这里并没有生效,这是因为函数在单独的环境中调用,它是指向window
的.所以我们这里可以借助词法作用域的特性来解决这个问题. 通过设定一个selt
来保存this
的值
function PromiseA(executor){
//添加属性
this.PromiseState = "pending";
this.PromiseResult = null;
//预先保存实例对象的this值
const self = this;
//resolve函数
function resolve(data){
//1.修改对象的状态(PromiseState)
self.PromiseState = "fulfilled";
//2.设置对象的结果值(PromiseResult)
self.PromiseResult = data;
}
function reject(data){
//1.修改对象的状态(PromiseState)
self.PromiseState = "rejected";
//2.设置对象的结果值(PromiseResult)
self.PromiseResult = data;
}
//同步调用一下
executor(resolve,reject);
}
reject
同理 ,就实现了我们上面的这一版
4. throw 抛出异常改变状态
这一节我们实现的状态是 throw
抛出异常会改变状态.
let p1 = new Promise((resolve,reject)=>{
throw "error"
});
console.log(p1)
Promise {: 'error'}
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "error"
可以看到官放的Promise
在其内部报错的时候,它会把内部的throw
处理掉,同时把状态修改,
把错误的结果放到PromiseResult
中.
function PromiseA(executor){
//添加属性
this.PromiseState = "pending";
this.PromiseResult = null;
//预先保存实例对象的this值
const self = this;
//resolve函数
function resolve(data){
//...
}
function reject(data){
//...
}
//-------------- 修改的代码-----------------
try{
//同步调用一下
executor(resolve,reject);
}catch (e){
//通过调用reject函数,它的内部可以修改状态的和赋值
//所以我们这里可以把错误直接传进去就可以了.
reject(e);
}
//-------------- 修改的代码-----------------
}
既然要处理错误,我们就添加一个try catch
然后通过reject
来处理内部的状态和数据.
看一下测试用例的输出:
let p1 = new PromiseA((resolve,reject)=>{
throw "error"
});
console.log(p1)
//PromiseA { PromiseState: 'rejected', PromiseResult: 'error' }
5.内部的状态只能修改一次
内部状态只能修改一次,就是说 我们先调用了resolve
,然后调用reject
,并不能把状态fulfilled
修改为了rejected
.
那么怎么实现呢? 只需要添加个判断就好
function PromiseA(executor){
//添加属性
this.PromiseState = "pending";
this.PromiseResult = null;
//预先保存实例对象的this值
const self = this;
//resolve函数
function resolve(data){
//-------------- 修改的代码-----------------
//判断状态
if(self.PromiseState !== "pending"){
return ;
}
//-------------- 修改的代码-----------------
//1.修改对象的状态(PromiseState)
self.PromiseState = "fulfilled";
//2.设置对象的结果值(PromiseResult)
self.PromiseResult = data;
}
function reject(data){
//-------------- 修改的代码-----------------
//判断状态
if(self.PromiseState !== "pending"){
return ;
}
//-------------- 修改的代码-----------------
//1.修改对象的状态(PromiseState)
self.PromiseState = "rejected";
//2.设置对象的结果值(PromiseResult)
self.PromiseResult = data;
}
//处理 throw 抛出的错误
try{
//同步调用一下
executor(resolve,reject);
}catch (e){
//通过调用reject函数,它的内部可以修改状态的和赋值
//所以我们这里可以把错误直接传进去就可以了.
reject(e);
}
}
测试用例: 可以看到我们的状态并没有被再次改变为rejected
let p1 = new PromiseA((resolve,reject)=>{
resolve("ok");
reject("error");
});
console.log(p1)
//PromiseA { PromiseState: 'fulfilled', PromiseResult: 'ok' }
6.then方法的实现
官方的then
方法接收两个函数的回调参数,分别对应于成功的情况和失败的情况.
当new Promise
的构造函数中调用resolve
,那么对应的then
方法中就会执行第一个回调函数.
当new Promise
的构造函数中调用reject
,那么对应的then
方法中就会执行第二个回调函数.
let p = new Promise((resolve,reject)=>{
throw "error"
});
p.then(value=>{
console.log(value)
},reason => {
console.log(reason)
})
所以我们的then
方法的两个形参 对应的 Promise
的两个实参. onResolved
和onRejected
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
}
既然构造函数 执行resolve
,就会调用onResolved
,所以需要在then
方法中进行调用.
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//调用 成功 回调函数
onResolved();
//调用失败的回调函数
onRejected();
}
但是这样调用的话,且不是两个都调用了,我们需要区分情况来对待.成功的时候调用onResolved
,
失败的时候调用onRejected
.所以应该怎么区分情况呢?
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//调用 成功 回调函数
if(this.PromiseState === "fulfilled"){
onResolved();
}
//调用失败的回调函数
if(this.PromiseState === "rejected"){
onRejected();
}
}
既然then
方法是构造函数执行完毕返回实例之后,通过实例调用的.这里就有了隐式绑定this
.
这个时候this
绑定的是实例对象p
.所以我们这个时候可以通过this
,拿到实例对象中的状态和数据.
PromiseState
和PromiseResult
.
then
方法的两个函数回调中,会返回实例对象执行结果的数据,所以我们在调用onResolved
和onRejected
的时候,给它们传入实例的执行结果 PromiseResult
.
所以最后就是这样:
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//调用 成功 回调函数
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
//调用失败的回调函数
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
}
7.异步任务 then 方法实现
异步任务的实现就是:
let p = new PromiseA((resolve,reject)=>{
//异步调用
setTimeout(()=>{
resolve("OK");
},100)
});
console.log(p);
p.then(value=>{
console.log(value)
},reason => {
console.log(reason)
})
//PromiseA { PromiseState: 'pending', PromiseResult: null }
我们之前在构造函数中都是同步调用resolve
,在执行的下面的then
的时候,实际上状态已经修改为
fulfilled
或者rejected
.
而异步调用的话,可以看上面的打印输出.实例的状态是pending
.它的状态是在未来的某个时间进行的改变.
而我们的then
方法中,却并没有对pending
状态的处理,所以我们需要给它添加一下.
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//调用 成功 回调函数
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
//调用失败的回调函数
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
//添加pending状态的处理
if(this.PromiseState === "pending"){
//...
}
}
那这里应该怎么处理呢?
由于是异步调用的原因,当同步执行到then
方法的时候,它的内部状态还是pending
,所以给同步代码使用的
//调用 成功 回调函数
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
//调用失败的回调函数
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
就用不上了,而内部状态的改变是在未来的某个地方进行的? 是在哪里呢?
function PromiseA(executor){
//添加属性
//....
//resolve函数
function resolve(data){
//判断状态
if(self.PromiseState !== "pending"){
return ;
}
//--------------- 在未来某个地方改变状态的就是这里----------
//1.修改对象的状态(PromiseState)
self.PromiseState = "fulfilled";
//2.设置对象的结果值(PromiseResult)
self.PromiseResult = data;
}
function reject(data){
//判断状态
//....
}
//处理 throw 抛出的错误
//...
}
所以我们需要在状态改变的地方去调用then
方法的回调. ,也就是需要一个变量把then
方法的两个回调函数
保存起来,然后在构造函数resolve
的改变状态的地方进行调用.所以我们定义一个callback
变量来存储回调参数.
function PromiseA(executor){
//添加属性
this.PromiseState = "pending";
this.PromiseResult = null;
//-------------- 修改的代码-----------------
this.callback = {};
//-------------- 修改的代码-----------------
//预先保存实例对象的this值
const self = this;
//resolve函数
function resolve(data){
//判断状态
if(self.PromiseState !== "pending"){
return ;
}
//1.修改对象的状态(PromiseState)
self.PromiseState = "fulfilled";
//2.设置对象的结果值(PromiseResult)
self.PromiseResult = data;
//-------------- 修改的代码-----------------
//调用成功的回调函数
if(self.callback.onResolved){
//参数是成功的结果
self.callback.onResolved(data);
}
//-------------- 修改的代码-----------------
}
function reject(data){
//判断状态
// ...
}
//处理 throw 抛出的错误
//....
}
我们在then
方法的中判断pending
来保存callback
;
//添加 then 方法
PromiseA.prototype.then = function (onResolved,onRejected){
//调用 成功 回调函数
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
//调用失败的回调函数
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
//-------------- 修改的代码-----------------
//添加pending状态的处理
if(this.PromiseState === "pending"){
//保存回调函数
this.callback = {
onResolved,
onRejected
};
}
//-------------- 修改的代码-----------------
}
测试用例:
let p = new PromiseA((resolve,reject)=>{
//异步调用
setTimeout(()=>{
resolve("OK");
},100)
});
p.then(value=>{
console.log(value)
},reason => {
console.log(reason)
})
//输出 OK