一、Promise是什么?
概念:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。
作用:主要通过链式调用解决地狱回调问题
二、手写封装promise及调用
1.封装promise
完整代码如下:
const PENDING = 'PENDING',
FUILLED = 'FUILLED',
REJECTED = 'REJECTED';
function resolvePromise(x,resolve,reject) {
// let called = false;
if((typeof x === 'object' && typeof x !== 'null') || typeof x === 'function') {
try {
let then = x.then;
if(typeof then === 'function') {
then.call(x,(y) => {
// if(called) return;
// called = true;
resolve(y)
},(r) => {;
reject(r)
})
} else {
resolve(x)
}
} catch(e) {
reject(e)
}
} else {
resolve(x)
}
}
class myPromise {
constructor(excutor) {
this.status = PENDING;
this.success = null;
this.reason = null;
this.resolveArray = [];
this.rejectArray = [];
const resolve = (res) => {
if(this.status === PENDING) {
this.status = FUILLED;
this.success = res;
}
this.resolveArray.forEach(fn => fn())
}
const reject = (rej) => {
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = rej;
}
this.rejectArray.forEach(fn => fn())
}
excutor(resolve,reject)
}
then (onFuilled,onRejected) {
let promise1 = new myPromise((resolve,reject) => {
if(this.status === FUILLED) {
setTimeout(() => {
try {
let x = onFuilled(this.success);
resolvePromise(x,resolve,reject)
} catch(e) {
// console.log(e,'eee')
onRejected(e)
}
},2000)
}
if(this.status === REJECTED) {
setTimeout(() => {
let x = onRejected(this.reason);
resolvePromise(x,resolve,reject)
},2000)
}
if(this.status === PENDING) {
this.resolveArray.push(() => {
onFuilled(this.success)
});
this.rejectArray.push(() => {
onRejected(this.reason)
});
}
});
return promise1
}
catch(callback) {
return this.then(null,callback)
}
}
module.exports = myPromise
2.Promise调用
代码如下(示例):
const myPromise = require ('./myPromise.js');
const p2 = new myPromise((resolve,reject) => {
resolve('my love')
});
const p3 = p2.then((res) => {
return new myPromise((resolve,reject) => {
reject('122222')
})
})
p3.then((res) => {
console.log(res, 'res444')
}).catch((e) => {
console.log(e,'e')
})
三. 封装思路解析
1. 基本特性
const p1 = new Promise((resolve,reject) => {
resolve("success")
})
p1.then(res => {
console.log(res,'res')
},(reason) => {
console.log(reason,'reason')
})
(1) new Promise包裹的是一个执行器,类似于excutor,且resolve和reject中两者只能执行一个,执行完后Promise的状态由pending更改成fuifilled(成功)或rejected(失败),且状态不可更改,此方法在new的时候会立即调用,所以可以封装在class的constructor函数中。
(2)then中包裹的其实是两个函数,第一个为成功的函数,第二个为失败的函数,两者中一次也只能调用其中一个,其调用哪个是根据promise实例的状态决定,即创建promise实例是调用resolve还是reject决定
(3)then的调用是异步调用,且在原生Promise中是微任务调用,在此封装时,用宏任务(setTimeOut)代替
封装代码如下
class myPromise {
constructor(excutor) {
this.status = PENDING;
this.success = null;
this.reason = null;
const resolve = (res) => {
if(this.status === PENDING) {
this.status = FUILLED;
this.success = res;
}
}
const reject = (rej) => {
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = rej;
}
}
excutor(resolve,reject)
}
then (onFuilled,onRejected) {
if(this.status === FUILLED) {
setTimeout(() => {
onFuilled(this.success);
},0)
}
if(this.status === REJECTED) {
setTimeout(() => {
onRejected(this.reason);
},0)
}
}
}
module.exports = myPromise
2. 异步调用问题
const myPromise = require ('./myPromise.js');
const p2 = new myPromise((resolve,reject) => {
setTimeout(() => {
resolve('my love')
},1000)
});
p2.then(res => {
console.log(res,'resss')
},reason => {
console.log(reason)
})
p2.then(res => {
console.log(res,'resss')
},reason => {
console.log(reason)
})
问题: 因为在执行then操作时,尚未执行到excutor执行器,所以在执行then时还是Pending状态,所以then中的两个方法调用失败
解决方法: 由于一个promise实例多次调用then时,其每次都会调用同一个状态的方法(resolve或者reject),这时我们可以将未来要调用的函数收集起来,然后等到真正执行resolve或者reject函数时去循环调用收集到的方法,因为方法是固定的,而变量的传参在执行resolve或reject的时候已经可以正确赋值了(发布订阅模式)
封装代码如下
1. construcor部分
this.resolveArray = [];
this.rejectArray = [];
const resolve = (res) => {
if(this.status === PENDING) {
this.status = FUILLED;
this.success = res;
}
this.resolveArray.forEach(fn => fn())
}
const reject = (rej) => {
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = rej;
}
this.rejectArray.forEach(fn => fn())
}
2. then部分
if(this.status === PENDING) {
this.resolveArray.push(() => {
onFuilled(this.success)
});
this.rejectArray.push(() => {
onRejected(this.reason)
});
}
3. then链式封装
原生特性:
(1) 之前能够链式调用的原因是因为then或者catch之后返回了一个promise实例,所以可以一直调用下去
(2) 调用then成功的场景: 返回promise成功结果或者普通的javascript代码
(3)调用then失败函数或者catch的场景:返回promise错误结果或者抛出错误
封装思路
(1)then中返回一个promise,并且收集调用结果,可能是个promise实例,也可能是其它数据类型;
(2)let x = x.then 调用,然后通过then.call的方法调用excutor方法,改变promise实例状态,传递值。
封装代码如下
1. then部分(部分示例)
then (onFuilled,onRejected) {
let promise1 = new myPromise((resolve,reject) => {
if(this.status === FUILLED) {
setTimeout(() => {
try {
let x = onFuilled(this.success);
// 递归调用
resolvePromise(x,resolve,reject)
} catch(e) {
// console.log(e,'eee')
onRejected(e)
}
},2000)
}
if(this.status === REJECTED) {
setTimeout(() => {
let x = onRejected(this.reason);
resolvePromise(x,resolve,reject)
},2000)
}
if(this.status === PENDING) {
this.resolveArray.push(() => {
onFuilled(this.success)
});
this.rejectArray.push(() => {
onRejected(this.reason)
});
}
});
return promise1
}
2. resolvePromise方法
function resolvePromise(x,resolve,reject) {
// let called = false;
if((typeof x === 'object' && typeof x !== 'null') || typeof x === 'function') {
try {
let then = x.then;
if(typeof then === 'function') {
then.call(x,(y) => {
// if(called) return;
// called = true;
resolve(y)
},(r) => {;
reject(r)
})
} else {
resolve(x)
}
} catch(e) {
reject(e)
}
} else {
resolve(x)
}
}
4. catch
catch其实是then的二次封装
封装代码
catch(callback) {
return this.then(null,callback)
}
和then方法第二个函数的区别
(1)就近调用
(2) 如果catch不调用,将会阻塞catch后的调用
5. 递归调用
setTimeout(() => {
try {
let x = onFuilled(this.success);
// 递归调用
resolvePromise(x,resolve,reject)
} catch(e) {
// console.log(e,'eee')
onRejected(e)
}
},2000)
总结
此次封装,主要针对promise的基本调用和异步调用,链式调用进行封装等.