形象的认识一下Promise
我们都知道在JavaScript中,代码都是单线程执行的,就是由上而下的依次执行,但是有些事情像网络操作、浏览器事件,都是异步执行的,简单来说就是当某个条件触发了才会去执行相应的方法函数(回调函数),就像这个例子,1秒后就会去执行对应的回调函数。
setTimeout(()=>{
console.log('执行了')
},1000)
那我们可以想象一下,如果说我去做一件事情要经过很多步骤,并且他们都是关联的,也就是做了这件事之后才能去做下一件事,而且还是异步的,可以瞧一下这个例子:
setTimeout(()=>{
console.log('我饿了')
setTimeout(()=>{
console.log('点了个外卖')
setTimeout(()=>{
console.log('外卖到了,我要吃饭了')
setTimeout(()=>{
console.log('我吃饱了,睡个觉...')
},10000)
},20000)
},1000)
},1000)
看上去是不是就像个阶梯,想要从一楼走到二楼,就得一个台阶一个台阶往上走,走的楼层多了,累了,自己都会有点懵逼,不知道自己到哪里了,为了解决这个问题呢?就有了电梯,那想要从一层到100层也是可以轻松搞定的是吧,那 Promise 就是那个电梯,坐上它不仅可以轻松地看到自己到了几层楼,还能通知每层的负责人去做相应的事情,假如说你需要去收齐1-100层所有的文件送到100层的老总那里,这时候你就可以坐上电梯到了一楼,等待一层的负责人送给你文件后,再到第二层...依次收齐最后到100层把文件送到老总那里,看上去是不是既轻松又有调理。就像这个:
function retPromise(msg){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(msg)
},1000)
})
};
let promise = retPromise("我坐上了电梯");
promise
.then(res=>{
return retPromise(res+'一层楼的文件')
})
.then(res=>{
return retPromise(res+'二层楼的文件')
},err=>{
console.log('呦!出故障了')
})
.then(res=>{
return retPromise(res+'三层楼的文件')
})
.then(res=>{
//我到100层了,也收集好了所有的文件
console.log(res) // 我坐上了电梯一层楼的文件二层楼的文件三层楼的文件
})
看上去是不是清晰了很多,最起码一眼就知道了我们一步一步的做了哪些什么事情,详细的用法可以去看看阮一峰老师写的ES6入门Promise,接下来我们分析一下Promise为什么能够做这些事情~
Promise原理剖析
- 特性,推荐先看一看
Promise A+ 规范- 它是一个类;
- 有三个状态:fulfilled(成功),rejected(失败),pending(等待中);
- 只有pending的时候可以改变状态,并且一旦改变完状态不可以再次更改;
- 实例化的时候会首先执行传入的函数;
- 实例有then方法;
- 实例能够链式调用;
- 简易代码实现
上面这段代码执行时,如果//声明三个状态 const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' class Promise { constructor(executor) { this.status = PENDING; let resolve = (value)=>{ //只有当状态为pending的时候才能改变 if(this.status === PENDING){ this.status = FULFILLED; this.value = value } } let reject = reason=>{ if(this.status===PENDING){ this.status = REJECTED; this.reason = reason; } } //如果说传入的函数执行中报错了直接reject结束 try{ executor(resolve,reject) }catch(err){ reject(err) } } then(onFulfilled,onRejected){ if(this.status===FULFILLED){ onFulfilled(this.value) } if(this.status===REJECTED){ onRejected(this.reason) } } }executor中没有指明是reject还是resolve的时候,那执行then方法的时候就是pending的状态,此时onFulfilled,onRejected是不能执行的,那会在这里卡住,所以我们需要判断当状态为pending的时候先把对应的onFulfilled,onRejected函数存起来,如果后面状态发生了更改,在一次性的去执行相应队列里面的函数,另外then函数也是不可以链式调用的,并且executor里面执行异步代码里改变状态的时候,then方法就已经先执行了,所以我们就需要运用一下宏任务微任务来调整一下函数的执行顺序,应该是resolve之后去执行相应的onFulfilled函数。new Promise((resolve,reject)=>{ console.log('hello') setTimeout(()=>{ resolve('dsdfd') }) }).then(()=>{ console.log('发现没有执行这里') })
代码稍加更改:
//声明三个状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class Promise {
constructor(executor) {
this.status = PENDING;
// 存储成功时的回调
this.onFulfilledCallback = [] //++++
// 存储失败时的回调
this.onRejectedCallback = [] //++++
let resolve = (value)=>{
//只有当状态为pending的时候才能改变
if(this.status === PENDING){
this.status = FULFILLED;
this.value = value
this.onFulfilledCallback.forEach(cb=>cb()) //++++
}
}
let reject = reason=>{
if(this.status===PENDING){
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallback.forEach(cb=>cb()) //++++
}
}
//如果说传入的函数执行中报错了直接reject结束
try{
executor(resolve,reject)
}catch(err){
reject(err)
}
}
then(onFulfilled,onRejected){
// 返回一个新的promise
let promise2 = new Promise((resolve,reject)=>{ //++++
if(this.status===FULFILLED){
// 这里就是为了解决executor函数里有异步函数执行改变状态的时候保证then里面的回调函数能够对应执行
setTimeout(()=>{ //++++
try{
onFulfilled(this.value)
} catch(err){
reject(err)
}
},0)
}
if(this.status===REJECTED){
setTimeout(()=>{
try{
onRejected(this.reason)
}catch(err){
reject(err)
}
},0)
}
if(this.status===PENDING){
this.onFulfilledCallback.push(()=>{
setTimeout(()=>{
try{
onFulfilled(this.value)
}catch(err){
reject(err)
}
},0)
})
this.onRejectedCallback.push(()=>{
setTimeout(()=>{
try{
onRejected(this.reason)
}catch(err){
reject(err)
}
},0)
})
}
})
return promise2;
}
}
现在就可以执行链式调用了,异步的一些任务对应的方法也可以正常运作了,但是当executor函数或者then函数里面的onFulfilled,onRejected方法执行的时候,如果return一个Promise实例或者一个值的时候是不能进行向下传递的,所以我们就应该对返回的值做一些操作,并且当onFulfilled,onRejected不是一个函数的时候,应该是也能正常执行的,就是值穿透,再次对代码做一些更改。
完整代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 对返回值x进行各种状态下的判断
function resolvePromise(promise2,x,resolve,reject){
if(x===promise2) return reject(new TypeError('Chaning cycle detected for promise '));
// 如果返回结果是一个promise
if((x&&typeof x === 'object')|| typeof x === 'function'){
let used;
try {
let then = x.then;
if(typeof then === 'function'){
then.call(x,y=>{
if(used) return;
used = true;
resolvePromise(promise2,y,resolve,reject)
},r=>{
if(used) return
used = true;
reject(r)
})
}else{
if(used) return;
used = true;
resolve(x)
}
}catch(err){
if(used) return;
used = true;
reject(err)
}
}else{
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.onFulfilledCallback = []
this.onRejectedCallback = []
let resolve = (value)=>{
if(this.status === PENDING){
this.status = FULFILLED;
this.value = value
this.onFulfilledCallback.forEach(cb=>cb())
}
}
let reject = reason=>{
if(this.status===PENDING){
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallback.forEach(cb=>cb())
}
}
try{
executor(resolve,reject)
}catch(err){
reject(err)
}
}
then(onFulfilled,onRejected){
// 值穿透,如果不是函数那我们就把它改造为函数让它能够正常的传递下去
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:res=>res;
onRejected = typeof onRejected === 'function'?onRejected:(err)=>{throw err};
let promise2 = new Promise((resolve,reject)=>{
if(this.status===FULFILLED){
setTimeout(()=>{
try{
const x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
} catch(err){
reject(err)
}
},0)
}
if(this.status===REJECTED){
setTimeout(()=>{
try{
const x = onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(err){
reject(err)
}
},0)
}
if(this.status===PENDING){
this.onFulfilledCallback.push(()=>{
setTimeout(()=>{
try{
const x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
}catch(err){
reject(err)
}
},0)
})
this.onRejectedCallback.push(()=>{
setTimeout(()=>{
try{
const x = onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(err){
reject(err)
}
},0)
})
}
})
return promise2;
}
catch(errFn){
return this.then(null,errFn)
}
finally(fn){
return this.then(fn,fn)
}
}
里面的catch和finally方法其实就是对then方法参数的改装,最终执行的还是then方法。 最后还需要添加一段代码,测试一下看看是否符合Promise A+规范:
Promise.deferred = ()=>{
let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
执行npx promises-aplus-tests promise.js如果全部为绿色则通过了这个规范
Promise.all的实现
function isPromise (p) {
return p instanceof Promise
}
Promise.all = (promises)=>{
return new Promise((resolve,reject)=>{
let ret = [];
let i = 0;
function processPromise(index,value){
ret[index] = value;
if(++i === promises.length){
resolve(ret)
}
}
for(let i =0;i<promises.length;i++){
if(isPromise(promises[i])){
promises[i].then(data=>{
processPromise(i,data)
},(err)=>{
reject(err)
return
})
}else{
processPromise(i,promises[i])
}
}
})
}
Promise.race的实现
Promise.race = (promises)=>{
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
if(isPromise(promises[i])){
promises[i].then(data=>{
return resolve(data)
},err=>{
reject(err)
return;
})
}else{
return resolve(promises[i])
}
}
})
}
哪里找源码?
GitHub地址 欢迎star^~^