Promise从入门到精通
概念
Promise是ES6引入的进行异步编程的新的解决方案
备注:旧方案是单纯的使用回调函数
- 从语法上来说:它就是一个构造函数,
- 从功能上来说:可以封装异步的任务,并且可以对结果进行处理
Promise最大的好处:可以解决回调地狱的问题,并且它在指定回调和错误处理这一块更加的灵活与方便
回调地狱的典型情形:
异步编程有哪些?
答:fs文件操作、数据库操作、AJAX、定时器等等
为什么要使用Promise?
指定回调函数的方式更加灵活
旧的方式:必须在启动异步任务前指定·promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定多个回调)
支持链式调用,可以解决回调地狱问题
回调地狱的缺点:1)不便于阅读;2)不便于异常处理
Promise的基本流程
promise的状态改变
状态指的是promise实例中的一个属性:【PromiseState】
pending 未决定的
fulfilled 成功
rejected 失败
1. pending => fulfilled
2. pending => rejected
promise对象的状态只有这两种改变,1个promise对象的状态只能改变一次,
不可能由fulfilled转rejected
也不可能由rejected转fulfilled
promise对象结果值属性介绍
实例对象中另一个属性:【PromiseResult】
保存的是成功/失败的结果
resolve可以修改PromiseResult的值
rejected可以修改PromiseResult的值
如何使用Promise?
API
1.Promise构造函数:Promise(exector){}
1)exector函数:称为执行器函数 (resolve,reject)=>{}
2)resolve函数:是Promise内部定义的,成功时我们调用resolve函数
3)reject函数:是Promise内部定义的,失败时我们调用reject函数
说明:exector会在Promise内部立即同步调用,异步操作在执行器中执行
2.Promise.prototype.then方法:(onResolved,onRejected)=>{}
1)onResolved函数:指定成功的回调函数 value=>{}
2)onRejected函数:指定失败的回调函数 reason=>{}
说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,返回一个新的promise对象
3.Promise.prototype.catch方法:(onRejected)=>{}
onRejected函数:指定失败的回调函数 reason=>{}
说明:then()的语法糖,相当于then(undefined,onRejected)
4.Promise.resolve方法 (value)=>{}
1)value:成功的数据或promise对象
说明:返回一个成功/失败的promise对象
作用:为了快速得到一个promise对象,而且能够封装1个值,将这个值转化为promise对象
如果传入的参数为非promise类型的对象,则返回的结果为成功的promise对象
如果传入的参数为promise对象,则参数的结果决定了resolve的结果
5.Promise.reject方法:(reason)=>{}
作用:快速返回一个失败的promise对象
返回的结果永远都是失败的promise对象
6.Promise.all方法:(promises数组)=>{}
参数:包含n个promise的数组
说明:返回一个新的promise对象,只有所有的promise都成功才成功,只要有一个失败就直接失败
7.Promise.race方法:(promises数组)=>{}
race有赛跑的意思
参数:包含n个promise的数组
说明:返回一个新的promise对象,第一个改变状态的promise就是最终的结果状态
promise的几个关键问题
1. 如何改变promise的状态?
(1) resolve(value): 如果当前是pending就会变为fulfilled
(2) reject(reason): 如果当前是pending就会变为rejected
(3) 抛出异常: 如果当前是pending就会变为rejected
2. 一个promise指定多个成功/失败回调函数, 都会调用吗?
当promise改变为对应状态时都会调用
3. 改变promise状态和指定回调函数谁先谁后?
(1) 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
(2) 如何先改状态再指定回调?
① 在执行器中直接调用resolve()/reject()
② 延迟更长时间才调用then()
(3) 什么时候才能得到数据?
① 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
② 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
4. promise.then()返回的新promise的结果状态由什么决定?
(1) 简单表达: 由then()指定的回调函数执行的结果决定
(2) 详细表达:
① 如果抛出异常, 新promise变为rejected, reason为抛出的异常
② 如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值
③ 如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果
5. promise如何串连多个操作任务?
(1) promise的then()返回一个新的promise, 可以开成then()的链式调用
(2) 通过then的链式调用串连多个同步/异步任务
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok');
},1000);
});
p.then(v=>{
return new Promise((resolve,reject)=>{
resolve('success');
});
}).then(v=>{
console.log(v);
}).then(v=>{
console.log(v);
})
6. promise异常传透?
(1) 当使用promise的then链式调用时, 可以在最后指定失败的回调,
(2) 前面任何操作出了异常, 都会传到最后失败的回调中处理
let p = new Promise((resolve,reject)=>{
reject('err');
});
p.then(v=>{
console.log(111);
}).then(v=>{
console.log(222);
}).then(v=>{
console.log(333);
}).catch(r=>{
console.log(r);
});
7. 中断promise链?
(1) 当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数
(2) 办法: 在回调函数中返回一个pendding状态的promise对象
代码展示
//声明函数 执行器 『同步回调』
function Promise(executor) {
//设置实例对象的属性
this.PromiseState = 'pending';
this.PromiseResult = undefined;
this.callbacks = [];
//声明函数
const success = (value) => {
if (this.PromiseState !== 'pending') return;
//修改状态为成功 PromiseState
this.PromiseState = 'fulfilled';
//设置成功的结果值 PromiseResult
this.PromiseResult = value;
//调用回调
if (this.callbacks.length > 0) {
this.callbacks.forEach(cbObj => {
//执行成功的回调
cbObj.ok();
})
}
}
const fail = (reason) => {
if (this.PromiseState !== 'pending') return;
//修改状态为失败 PromiseState
this.PromiseState = 'rejected';
//设置成功的结果值 PromiseResult
this.PromiseResult = reason;
//调用回调
if (this.callbacks.length > 0) {
this.callbacks.forEach(cbObj => {
//执行成功的回调
cbObj.ng();
})
}
}
try {
//调用执行器函数
executor(success, fail);
} catch (e) {
//调用 fail 函数
fail(e);
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
//判断 异常穿透
if(typeof onRejected !== 'function'){
onRejected = reason => {throw reason};
}
//值传递
if(typeof onFulfilled !== 'function'){
onFulfilled = value => value;
}
return new Promise((resolve, reject) => {
//封装函数简化代码
let callback = (type) => {
try {
let res = type(this.PromiseResult);
//判断
if (res instanceof Promise) {
res.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
resolve(res);
}
} catch (e) {
reject(e);
}
}
//判断 成功
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onFulfilled);//
})
}
//失败
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected);
})
}
//pending
if (this.PromiseState === 'pending') {
//保存回调
this.callbacks.push({
ok: function () {
//成功
callback(onFulfilled);
},
ng: function () {
callback(onRejected);
}
})
}
});
}
Promise.prototype.catch = function(onRejected){
return this.then(undefined, onRejected);
}
Promise.resolve = function(value){
return new Promise((resolve,reject) => {
//判断
if(value instanceof Promise){
//value.then(resolve, reject); 这行代码和下面5行代码功能一样
value.then(v=>{
resolve(v);
}, r => {
reject(r);
});
}else{
resolve(value);
}
})
}
Promise.reject = function(reason){
return new Promise((resolve, reject) => {
reject(reason);
})
}
Promise.all = function (promiseArr) {
return new Promise((resolve, reject) => {
let count = 0;
let arr = [];
for (let i = 0; i < promiseArr.length; i++) {
promiseArr[i].then(v => {
count++;
arr[i] = v;
if (count === promiseArr.length) {
resolve(arr);
}
}, r => {
reject(r);
});
}
});
}
Promise.race = function (promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(item => {
item.then(v => {
resolve(v);
}, r => {
reject(r);
});
});
});
}
面试题1:
let p = new Promise((resolve, reject) => {
reject("error");
});
p.then(v => {
console.log(v);
}).then(v => {
console.log(v);
}, r => {
console.log(r); // error
}).then(v => {
console.log(v); // undefined
}, r => {
console.warn(r);
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。