promise
概念
一种异步编程的思路,一种以同步形式来书写异步代码的技术
状态
promise一共有3种状态
- pending 进行中
- fulfilled 已成功
- rejected 已失败
状态改变流程
- pending -> fulfilled
- pending -> rejected
注意
- 不受外界影响
- 一旦改变无法再改变
基本功能
-
创建promise对象
let p = new Promise((resolve,reject)=>{ resolve('状态成功') }) // 状态成功
-
创建promise对象状态只能改变一次
let p = new Promise((resolve,reject)=>{ resolve('状态成功') reject('状态失败') }) // 状态成功
-
创建的promise对象可以调用then方法
let p = new Promise((resolve,reject)=>{ resolve('状态成功') }) p.then(res=>{ console.log(res,'then成功1'); },err=>{ console.log(err,'then失败1'); }) p.then(res=>{ console.log(res,'then成功2'); },err=>{ console.log(err,'then失败2'); }) p.then(res=>{ console.log(res,'then成功3'); },err=>{ console.log(err,'then失败3'); }) // 状态成功 then成功1 // 状态成功 then成功2 // 状态成功 then成功3
-
创建的promise对象(异步)也可以调用then方法
let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('状态成功') },1000) }) p.then(res=>{ console.log(res,'then成功1'); },err=>{ console.log(err,'then失败1'); }) // 状态成功 then成功1
-
promise-resolve执行过程是一个异步任务
let p = new Promise((resolve, reject) => { console.log('同步1'); resolve('状态成功'); console.log('同步2'); }) console.log('同步3'); p.then(res => { console.log(res, 'then成功'); }, err => { console.log(err, 'then失败'); }) console.log('同步4');
-
创建的promise对象在不同时机都可以调用then方法
// 1.异步请求完毕之后注册成功、失败回调函数 let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('状态成功') },1000) }) setTimeout(()=>{ p.then(res=>{ console.log(res,'then成功'); },err=>{ console.log(err,'then失败'); }) },2000) // 状态成功 then成功
// 2.异步请求完毕之前注册成功、失败回调函数 let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('状态成功') },2000) }) setTimeout(()=>{ p.then(res=>{ console.log(res,'then成功'); },err=>{ console.log(err,'then失败'); }) },1000) // 状态成功 then成功
-
创建的promise对象可以链式调用then方法1(返回undefind、普通值)
let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('状态成功') },1000) }) p.then(res=>{ console.log(res,'then成功1'); },err=>{ console.log(err,'then失败1'); }).then(res=>{ console.log(res,'then成功2'); },err=>{ console.log(err,'then失败2'); }).then(res=>{ console.log(res,'then成功3'); },err=>{ console.log(err,'then失败3'); }) // 状态成功 then成功1 // undefined then成功2 // undefined then成功3
-
创建的promise对象可以链式调用then方法2(返回promise实例)
let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('状态成功1') },1000) }) p.then(res=>{ console.log(res,'then成功1'); return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('状态成功2') },1000) }) },err=>{ console.log(err,'then失败1'); }).then(res=>{ console.log(res,'then成功2'); },err=>{ console.log(err,'then失败2'); }).then(res=>{ console.log(res,'then成功3'); },err=>{ console.log(err,'then失败3'); }) // 状态成功 then成功1 // 状态成功2 then成功2 // undefined then成功3
-
处理异常-then中禁止返回自己
let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('状态成功') },1000) }) let p2 = p.then(res=>{ console.log(res,'then成功1'); return p2 },err=>{ console.log(err,'then失败1'); }) p2.then(res=>{ console.log(res,'then成功2'); },err=>{ console.log(err,'then失败2'); }) // 状态成功 then成功 // 禁止返回自己,抛出错误promise的错误 'then失败2'
-
异常处理-then的成功回调函数参数为非函数
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('状态成功')
}, 1000)
})
p.then('ok', err => {
console.log(err, 'then失败1');
}).then(()=>{
console.log(res,'then成功2')
}, err => {
console.log(err, 'then失败2');
}).then(res => {
console.log(res, 'then成功3');
}, err => {
console.log(err, 'then失败3');
})
// 状态成功 then成功2
// undefined 'then成功3'
-
catch可以捕获之前所有异常
let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('状态成功') },1000) }) p.then(res=>{ console.log(res,'then成功1'); throw '抛出报错' }).then(res=>{ console.log(res,'then成功2'); }).then(res=>{ console.log(res,'then成功3'); }).catch(err=>{ console.log(err,'catch失败-抓取之前所有错误,并继续执行'); }).then(res=>{ console.log(res,'then成功4'); }) // 状态成功 then成功1 // 抛出报错 catch失败-抓取之前所有错误,并继续执行 // undefined 'then成功4'
-
快捷返回一个成功的promise实例
let p = Promise.resolve('状态成功'); p.then(res=>{ console.log(res,'then成功'); },err=>{ console.log(err,'then失败'); }) // 状态成功 then成功
-
快捷返回一个失败的promise实例
let p = Promise.reject('状态失败'); p.then(res=>{ console.log(res,'then成功'); },err=>{ console.log(err,'then失败'); }) // 状态失败 then失败
-
处理所有promise实例 all 方法
let p = Promise.all([p1,p2,p3,p4]) p.then(res=>{ console.log(res,'then成功'); },err=>{ console.log(err,'then失败'); }) // [1结果,2结果,3结果,4结果] then成功
-
获取所有promise实例中最快的 race 方法
let p = Promise.race([p1,p2,p3,p4]) p.then(res=>{ console.log(res,'then成功'); },err=>{ console.log(err,'then失败'); }) // 1、2、3、4中最快的结果 then成功
实现过程和代码
// 准备常量,防止使用过程中被改动
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise {
constructor(executor) {
// promiseA+ 规定 executor 执行器内置 resolve和reject
// 记录状态
this.status = PENDING;
// 记录成功返回值
this.value = undefined;
// 记录失败返回值
this.reason = undefined;
// promise为异步时,status为pending等待结果,需要先记录then里面的成功回调函数,等待结果出来后一起执行
this.onFulfilledCallbacks = [];
// promise为异步时,status为pending等待结果,需要先记录then里面的失败回调函数,等待结果出来后一起执行
this.onRejectedCallbacks = [];
// 成功回调
const resolve = (data) => {
// 状态只能改变一次,如果状态改变过就不允许再次改变
if (this.status !== PENDING) return;
// 如果成功,改变状态和返回值
this.status = FULFILLED;
this.value = data;
// 如果之前保存了then里面的回调,这里遍历数组执行里面的函数
// 保证以下执行顺序为1、3、2,必须使用回调函数
// 1、3是先放到数组中同步执行,等待结果同步执行,2是异步等1出来结果后再执行
// p.then(res => {
//
// })
// .then(res => {
//
// })
// p.then(res => {
//
// })
if (this.onFulfilledCallbacks.length <= 0) return
// 这里的异步放在push函数处效果一致
// 新问题1,这里的回调好像没有意义,同理失败回调也是一样
// 问题详情:new Promise中已经是异步,所以这里回调没必要是异步,只有同步的时候才会出现需要包裹异步的问题
setTimeout(() => {
this.onFulfilledCallbacks.forEach((fn) => fn());
})
};
// 失败回调
const reject = (data) => {
if (this.status !== PENDING) return;
// 如果失败,改变状态和返回值
this.status = REJECTED;
this.reason = data;
if (this.onRejectedCallbacks.length <= 0) return
setTimeout(() => {
this.onRejectedCallbacks.forEach((fn) => fn());
})
};
// 防止new Promise执行过程中就报错
try {
executor(resolve, reject);
}
catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
// then的步骤三:then的异常/特殊处理
// 需提前理解:
// 上一个then执行的成功或者失败的结果并不影响下一个then的走向,只有返回的结果才影响后续then的走向
// 3.1 如果返回值为普通值、undefined(没填)等 后续走成功
// 3.2 如果返回值为异常、Promose.reject() 后续走失败
// 1.
// 情况:正常流程,遇到报错时,then又缺少失败回调,利用catch抓取之前所有的错误
// 代码:then(() => { }).then(() => { }).catch() 相当于 .then(() => { }, undefined).then(() => { }, undefined).catch()
// 解释:如果报错,then中没有失败回调then(() => { },undefined),会导致失败回调返回 undefined ,返回值为undefined其实又会走回成功回调,导致出现问题,所以要保证在没有填写失败回调的时候,依然能够继续走失败
// 2.
// 情况:调用 .catch回调,本质也是调用 .then()。当想catch捕获错误,但前面没有错误时,catch的then就会走到then(undefined,()=>{})的undefined上,最终走到下一个成功,但值也要传递过去
// 代码:.then(res=>{ return 没有错误都成功了 }).catch(()=>{}) 相当于 .then(res=>{ return 没有错误都成功了 }).then(undefined,() => { })
// 解释:原本已经成功,但是需要把上一个值,传递到下一个then中,undefined无法传递,所以要有变量接收,再return返回才行
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected = typeof onRejected === "function" ? onRejected : (reason) => { throw reason };
let promise2 = new Promise((resolve, reject) => {
// then的步骤二:promise需要链式编程,then的返回值可以调用then,所以每一个then都必须返回一个新的promise实例,才可以调用then方法
// then().then()
// 进入then后,执行new promise也是同步,不用担心会影响内部3种状态的执行顺序
// 链式调用then的时候,后面一个then的成功或失败,取决于上一个then的执行结果,重要:与上一个执行的成功回调或者失败回调无关,只跟return的内容有关
// 2.1 如果返回的是 undefined(等于没有返回值)、数字、字符串都是代表成功
// 2.2 如果返回的是 throw reason 和 Pormise.reject 都代表是失败
// onFulfilled函数(上一个then的回调函数),需要得到他的结果,才可以触发resolve函数(下一个then的回调开关)
// 所以 x = onFulfilled 的结果x,是决定resolve的关键
// 但是x也许有很多情况,所以也要分类,接 resolvePromise 的封装
// then的步骤一:进入then的时候status的分3种状态
// 1.1 status状态1:之前new promise是同步,promise已经有结果,状态已经改变为fulfilled
if (this.status === FULFILLED) {
// promise 在调用then函数的时候是同步,但在执行then里面的成功、失败回调的时候是异步,所以加上定时器(防止promise2(同步) 未生成时就和x进行比较)
setTimeout(() => {
// onFulfilled(this.value);
// then步骤四:封装
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err);
}
});
}
// 1.2 status状态2:之前new promise是同步,promise已经有结果,状态已经改变为rejected
if (this.status === REJECTED) {
setTimeout(() => {
// onRejected(this.reason);
// 封装
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err);
}
});
}
// 1.3 status状态3:之前promise是异步,promise还未有结果,状态还未改变(pending)。
if (this.status === PENDING) {
// 保存成功回调:此时promise还没有结果,所以可以先把then里面的成功回调都收集起来,等待状态改变后一起执行
this.onFulfilledCallbacks.push(() => {
// 这里没有直接push then里面的回调函数,而是在外面额外包裹了一层函数,是因为可以将参数一并保存起来
// onFulfilled(this.value)
// 封装
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err);
}
});
// 保存失败回调
this.onRejectedCallbacks.push(() => {
// onRejected(this.reason)
// 封装
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err);
}
});
}
});
return promise2;
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
static reject = (reason) => {
return new Promise((resolve, reject) => {
reject(reason)
})
}
static resolve = (value) => {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(resolve,reject)
} else {
resolve(value)
}
})
}
static all = (promiseArr) => {
return new Promise((resolve, reject) => {
let count = 0;
let resArr = [];
promiseArr.forEach((item,index) => {
item.then(res => {
resArr[index] = res;
count++
if (count === promiseArr.length) {
resolve(resArr)
}
}, err => {
reject(err)
})
})
})
}
static race = (promiseArr) => {
console.log(promiseArr);
return new Promise((resolve, reject) => {
for (let p of promiseArr) {
p.then(resolve,reject)
}
})
}
}
const resolvePromise = (promise2, x, resolve, reject) => {
// 该方法的核心运行得到x的结果,再根据x的结果驱动后面链式的then方法中的成功或失败回调
// 1. 如果返回promise2自己:抛出错误。
// 2. 如果返回promise对象(写法2:包括其他可能是promise结构的函数、对象等)
// 2.1 如果返回可能是promise对象,有then方法,那么promise本身也要得到结果才可以驱动then中的new promise方法,再得到结果,驱动后面链式的then方法,所以多一步
// 2.2 如果返回可能是promise对象,没then方法,把他当普通对象处理:直接触发resolve,触发后续的then方法
// 3. 如果返回普通值,直接触发resolve方法,触发后续的then方法
// 4. 如果报错,那么直接调用reject方法,触发后续的then方法
// x状态的1:是promise2自己。处理循环调用,并不是死循环,只是防止promise会返回自己
if (x === promise2) {
// 这里不仅抛出错误,还要return阻止代码继续运行(原生抛出错误,自己封装不抛出错误,推测:自己封装抛出的错误被reject捕获、原生的错误是被浏览器识别报出)
return reject(new TypeError('禁止返回自己'))
}
// //实现版本一:简单版开始
// // 2. x的状态2:是promise对象
// if (x instanceof Promise) {
// // 正常写法:
// // x.then(data => {
// // resolve(data)
// // }, err => {
// // reject(err)
// // })
// // 简写:
// // x.then(resolve, reject);
// // 理解1:
// // const fn = data =>resolve(data)
// // x.then(fn)
// // fn是下一个then里面的resolve回调函数,本身带了 res=>resolve(res)
// // 理解2:
// // const fn = data => { resolve(data) }; ele.onclick = () => { fn(data) }
// // ele.onclick = fn;
// // fn就是整个函数体,里面包含了参数。这里fn 就是 resolve函数体,等于直接把函数体传入。
// // 这里应该还少调用一层,成功或失败之后,再调用里面的resolve、reject函数
// } else {
// // 3. x的状态3:是普通值
// resolve(x);
// }
// //实现版本一:简单版结束
// 实现版本二:正规写法
// 2. x的状态2:promise对象,包括其他可能是promise结构的函数、对象等(普通对象、普通函数,他们身上都可能有then方法,只要不是null都可以进)
// 版本一版本二对比 x instanceof Promise只能判断是否为promise对象,不包括对象、函数中有有then的方法,考虑没那么全面
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 问题1:called开关的作用是什么?而且必须要!
// 自己理解:是防止promise如果走了成功或者失败回调后,突然报错,又走向catch的reject?到底是防止哪些多次回调??流程是什么?
let called;
try {
let then = x.then;
// 2.1 返回可能是promise对象,有then方法
if (typeof then === 'function') {
// 问题2:为什么必须要then.call,而不直接调x.then!防止函数内的this指向其他吗?
// 问题3:原生返回promise实例,在内部会 p2.then().then() 处理两次,我代码只p2.then() 处理一次处理
// 效果正常,检测报错 x.then().then(resolve,reject)
// 检测正常,运行出错 如下代码:
then.call(x, y => {
if (called) return
called = true;
// 这里是防止promise实例嵌套,返回的还是promise对象,多层嵌套
resolvePromise(promise2, y, resolve, reject)
}, err => {
if (called) return
called = true;
reject(err)
})
} else {
// 2.2 返回可能是promise对象,没then方法
resolve(x)
}
} catch (error) {
// 4. 报错
if (called) return
called = true;
reject(error)
}
} else {
// 3. 返回普通值
resolve(x)
}
};
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject
})
return dfd
}
// module.exports = Promise
以上代码可以正常运行(静态方法不是特别严谨)并且可以通过检测,但其中有几个地方的思路未理顺(实现代码中出现了3个问题),如果有老大阅读,欢迎指定,谢谢
实现过程中的注意事项
- .then(成功回调,失败回调) 中的失败回调很少用,因为他只能捕获上一个promise实例的错误,而使用.catch() 可以捕获之前所有promise实例和函数内部错误
- new Promise中的resolve和reject为同步时, 会异步执行then里面的回调函数
new Promise中的resolve和reject为异步时, 会同步把then里面的回调函数保存到数组中,再异步执行数组内容 - then() 的执行是同步,但.then(成功回调,失败回调)里面的回调函数是异步的(1.默认机制 2.因为then里面需要返回 retrun new Promise的实例,必须等他new完,所以这里有一个定时器)
- .then()里面的回调函数,成功和失败的执行,只取决于上一个then里面回调的 返回值(跟之前是成功回调还是失败的回调没关系)
后续执行成功回调的返回值为:undefined、数字、字符串都是代表
后续执行失败回调的返回值为:throw reason 和 Pormise.reject 都是代表 - promise 一般会在失败的最后加一个.catch() 来捕获之前所有的错误,这并不是一下子跳转过来的,而是逐级执行错误回调。这叫穿透(reason=> throw reason 或者 return Promise.reject(reason) 抛错误或者给一个失败回调)
- 需要中断promise,可以返回一个pending状态的promise实例