学习准备:
非常适合初学
- js同步异步知识
- 类的了解
- 原型相关知识
- 对promise有相关的使用
先看看基础用法
成功语法格式:
function test() {
return new Promise((resolve, reject) => {
resolve(123)
})
}
test().then(v =>console.log("成功:",v))
失败语法格式:
function test() {
return new Promise((resolve, reject) => {
reject('error')
})
}
test().then(v =>console.log("成功:",v),r=>console.log('失败:',r))
或者:
function test() {
return new Promise((resolve, reject) => {
reject('error')
})
}
test().catch(r=>console.log('失败:',r))
- 可以看到失败处理函数的两种写法的结果是相同的
- 失败和成功 最简单的两种使用方法就是以上内容
- 下面开始一步步深入
我们先初步写出我们的promise
平时实例化的时候我们是这样的:
- 传参,然后在内部接收参数
-
constructor 负责处理实例化时传递过来的参数
class Person {
constructor(Pname){
this.name = Pname;
}
}
let res = new Person('tom');
console.log(res)
- 下面的 MyPromise 是我们自己写的类
- 先看看我们怎么使用
- 实例化 然后传参过去
function run() {
return new MyPromise((resolve, reject) => {
resolve('成功')
})
}
run().then(v => console.log(v))
- 这里传参我们只是把普通参数 换成了一个函数而已
- 这下能看清楚吧
- 注意这里是一个传的是这个 函数
new MyPromise( (resolve, reject) => {} )
(resolve, reject) => { }
-
注意你传过来的那个函数 它还要接收两个参数 resolve, reject
-
再看看我们咋写的
-
它里面调用了 resolve或reject,说明这个参数也是一个函数,不然它怎么可能调用呢?
return new MyPromise((resolve, reject) => {
resolve('成功')
})
-
既然传递了参数,那么就要处理一下参数
class MyPromise {
constructor(callBack) {
// callBack 就等同于 (resolve, reject) => { }
//callBack() 不就是在调用这个传过来的函数吗
//(resolve, reject) => { } 不是接收两个参数吗
//但是这两个参数 它还要拿去调用,所以传两个函数给他
callBack(this.resolve, this.reject);
}
// 在调用他们的时候是不是传了一个值啊,
//所以在你声明函数的时候接收一下 你调用函数时 传过来的值
//resolve (成功信息)
//reject (失败信息)
resolve = (value) => {
};
reject = (value) => {
};
}
-----------------------------------到这里没有理解的,再消化一下--------------------------------
分析:这个值怎么就可以出现在这里呢?这里是怎么拿到的呢?
- 我们是不是写了这么个东西
- 这样的话 你 调用 resolve(值 ) 或者 reject(值 )
- 下面是不是就可以接收到了
- 下面的函数声明在类里面,那么类里面不就收到了 成功 或者 失败 的传值了吗
resolve = (value) => {
};
reject = (value) => {
};
那么就再追加一些代码
- 我们把传过来的值保存起来
- 为什么要这样存起来呢,下面就知道了。
class MyPromise {
constructor(callBack) {
this.data = null;//用来装 resolve() 或者 reject()调用传过来的值
callBack(this.resolve, this.reject);
}
resolve = (value) => {
this.data = value;
};
reject = (value) => {
this.data = value;
};
}
到此先看看写了个什么玩意
console.log(new MyPromise((resolve, reject) => {}))
再来看看我们的使用方式
function run() {
return new MyPromise((resolve, reject) => {
resolve('成功')
})
}
run().then(v => console.log(v))
上面那种方式生疏的,可以看看下面这种写法,差不多一个意思
let s = new MyPromise((resolve, reject) => {
resolve('成功')
})
s.then(v => console.log(v))
- 这里是实例化后,在实例化对象中调用 then() 方法
- 这里把then方法写在外面,也可以写在类里面
- 这里的this指向实例
- 通过this.data呢,拿到你 resolve()调用时传过去的参数
MyPromise.prototype.then = function (resolveCall) {
//resolveCall 就等同于函数 v => console.log(v)
//resolveCall ()不就是在调用这个函数吗
resolveCall(this.data)
}
实验一下
let s = new MyPromise((resolve, reject) => {
resolve('成功的值')
})
s.then(v => console.log(v))
梳理一下:
- this.data 就相当于一个公共部分的数据
- resolve 或 reject 传递一个参数 将这个data 赋值
- 在 then 方法中取出这个 data 然后调用回调函数 把这个 data 传出去
- 至此 不就解决问题了
-----------------------------------到这里没有理解的,再消化一下--------------------------------
异步问题
- 我们知道有时候 我们要等到一个ajax请求返回之后我们才回去调用 resolve 或者 reject函数
- 这样的话,我们任然用上面的方式去使用一下
- 这里的请求 我就用定时器代替一下
- 这里的回调函数传参上面讲过了,不再赘述了
function request(cb) {
setTimeout(() => {
cb()
})
}
- 定时器执行的时候就是在执行 resolve 方法
class MyPromise {
constructor(callBack) {
this.data = null;
callBack(this.resolve, this.reject);
}
resolve = (value) => {
this.data = value;
};
reject = (value) => {
this.data = value;
};
}
MyPromise.prototype.then = function (resolveCall) {
resolveCall(this.data)
}
function run() {
return new MyPromise((resolve, reject) => {
request(() => {
resolve('成功')
})
})
}
run().then(v => console.log('成功返回的值',v))
可以看到结果并不是我们想要的
为什么会出现这种情况呢?
- 按照原本的执行顺序是: resolve-->存放值, then-->拿到值然后传递出去
- 这里由于有一个定时器,开启了异步任务,将 resolve-->存放值 这一个操作放到一边去,暂时不执行
- 然后执行 then-->拿到值然后传递出去,哎 由于 resolve-->存放值 没有执行的
- 所以 then-->拿到值然后传递出去 它拿到的值肯定就是初始化的时候的值 null
- 看一下初始化的操作
constructor(callBack) {
this.data = null;
callBack(this.resolve, this.reject);
}
那咋整呢?用下面的状态来解决这个问题
了解状态
这里我用 resolved rejected 去代表两种状态,方便对应相关函数
状态不可逆,一旦改变不可以回到原来状态
- pendding 初始化状态
- resolved 成功状态
- rejected 失败状态
下面我们将原来的代码改动一下
constructor(callBack) {
this.data = null;
this.state = 'pendding';//初始化状态
this.resolveList = [];//存放then方法中成功的回调函数
this.rejectList = [];//存放then方法中失败的回调函数
callBack(this.resolve, this.reject);
}
resolve = (value) => {
if (this.state !== 'pendding') return;//已经改变的 就不再执行下面
this.state = 'resolved';//改变状态为自己对应的状态
this.data = value;
this.resolveList.length > 0 && this.resolveList.forEach(resolveCall => resolveCall(value))
};
reject = (value) => {
if (this.state !== 'pendding') return;
this.state = 'rejected';
this.data = value;
this.rejectList.length > 0 && this.rejectList.forEach(rejectCall => rejectCall(value))
};
- 看下面就知道为什么要这个数组来存放函数了
MyPromise.prototype.then = function (resolveCall) {
//如果调用then方法的时候 状态还没有改变,
//或者说如果调用then方法的时候,resolve还没有执行
//我们把then方法中的回调函数暂时存起来
if (this.state === 'pendding') this.resolveList.push(resolveCall)
else resolveCall(this.data)
}
看下面这种形式,then方法还可以有第二个参数,第二个参数是处理失败的一个回调函数
function test() {
return new Promise((resolve, reject) => {
reject('error')
})
}
test().then(v =>console.log("成功:",v),r=>console.log('失败:',r))
所以改动一下 then 方法
MyPromise.prototype.then = function (resolveCall,rejectCall) {
if (this.state === 'pendding') {
this.resolveList.push(resolveCall);
this.rejectList.push(rejectCall)
}
else resolveCall(this.data)
}
梳理一下:
首先这么改动以后为什么就解决了上面的问题呢?
再把前面的问题放出来:
resolve = (value) => {
if (this.state !== 'pendding') return;
this.data = value;//存值
};
MyPromise.prototype.then = function (resolveCall,rejectCall) {
resolveCall(this.data);//将内部存的值,传出去
}
function request(cb) {
setTimeout(() => {
cb()
})
}
function run() {
return new MyPromise((resolve, reject) => {
request(() => {
resolve('失败');//传值到 resolve函数,它收到会存放
})
})
}
run().then(v => console.log('成功返回的值', v))
- 这里结果是 null 不用多说
- 按照原本的执行顺序是: resolve-->存放值, then-->拿到值然后传递出去
改动之后:
- state初始化为 pendding
- 先记住一点,只有调用resolve或者reject状态才会改变
- resolve-->存放值 被当作异步任务放在一边
- 执行 then-->拿到值然后传递出去 ,但是发现 状态没变化,还是初始化的状态,也就是还没有执行 resolve或者reject
- 那就把 then-->拿到值然后传递出去 这个操作给我存起来,等到执行 resolve或者reject 的时候我再执行它
- 因为等到 执行 resolve或者reject 的时候 值是不是已经传进来了,状态也会发生相应的改变
这样就解决了异步的问题
先写到这里,第一次写不知道有没有叙述清楚,如有不足请指点出来,如果觉得还不错,后面会发后续完善部分
大家的支持是我最大的动力