在学习王世元老师的vue教程过程中,穿插着复习了一下Promise的内容。
引言
①什么是Promise?
一个ES6中非常重要和好用的特性
②Promise的用途?
Promise是异步编程的一种解决方案
③什么时候会处理异步事件?
但是网络请求非常复杂时,就会出现回调地狱
网络请求的回调地狱
看似只是几层回调函数的嵌套,但是在实际数据请求的时候我,可能并不是这么简单。
ex:每次调用前实际都会进行很多其他的操作
若在每个回调函数中除了回调另外一个函数,还有如以上例子一样的其他操作,就会使得代码变得臃肿,进而难以维护。
问题所在:
如何避免这个问题?
利用Promise完美的解决这个问题。
Promise实际是对异步操作的封装。
先来体会一下链式编程的思想(还没有真正实际使用Promise,只是用它来封装)
回调地狱举例如下:
我们将setTimeout模拟成网络请求
①
<script>
// 1.使用setTimeout
// setTimeout(() => {
// console.log('hello');
// }, 1000);
// 参数 -> 函数(resolove,reject)
// resolve,reject本身又是函数
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Hello world');
console.log('Hello world');
console.log('Hello world');
console.log('Hello world');
console.log('Hello world');
setTimeout(() => {
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
setTimeout(() => {
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
}, 1000);
}, 1000);
}, 1000);
})
</script>
②为了避免回调地狱,这部分代码就需要被抽离。
// 参数 -> 函数(resolove,reject)
// resolve,reject本身又是函数
new Promise((resolve, reject) => {
setTimeout(() => {
// 一旦调用resolve(),则会在Promise后面自动调用.then(),
//而then()中也是一个函数
resolve()
}, 1000);
}).then(()=>{
})
③再将刚刚出现回调地狱的代码放入then()中
new Promise((resolve, reject) => {
setTimeout(() => {
// 一旦调用resolve(),则会在Promise后面自动调用.then(),而then()中也是一个函数
resolve()
}, 1000);
}).then(()=>{
console.log('Hello world');
console.log('Hello world');
console.log('Hello world');
console.log('Hello world');
console.log('Hello world');
setTimeout(() => {
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
setTimeout(() => {
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
}, 1000);
}, 1000);
})
④但是这里依旧还是有回调地狱,因此继续抽离。
new Promise((resolve, reject) => {
// 第一次网络请求的代码
setTimeout(() => {
// 一旦调用resolve(),则会在Promise后面自动调用.then(),而then()中也是一个函数
resolve()
}, 1000);
}).then(()=>{
// 第一次拿到结果的处理代码
console.log('Hello world');
console.log('Hello world');
console.log('Hello world');
console.log('Hello world');
console.log('Hello world');
return new Promise((resolve,reject)=>{
// 第二次网络请求的代码
setTimeout(() => {
resolve()
}, 1000);
}).then(()=>{
// 第二次处理的代码
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
return new Promise((resolve,reject)=>{
// 第三次网络请求的代码
setTimeout(() => {
resolve()
}, 1000);
}).then(()=>{
// 第三次处理的代码
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
})
})
})
是不是感觉这样书写以后更加复杂了?
但是仔细看,虽然代码变得复杂了,但是逻辑上却更加清晰,每一次网络请求都是放在一个Promise对象内部。(请求代码在Promise内部,而请求的处理代码则是在Promise.then中)
什么时候用到Promise?
一般是有异步操作时,使用Promise对这个异步操作进行封装
//模板
// new ->构造函数(1.保存某些状态信息 2.执行传入的函数)
// 在执行传入的回调函数时,会传入两个参数,resolve,reject,而这两个又是函数
new Promise((resolve,reject)=>{
setTimeout((data) => {
// resolve中传什么,后面的then中就传什么
resolve(data)
}, 1000);
}).then((data)=>{
})
Promise的基本使用代码
new Promise((resolve,reject)=>{
setTimeout(() => {
// resolve中传什么,后面的then中就传什么
resolve('Hello World')
}, 1000);
}).then((data)=>{
//处理代码
console.log(data);
console.log(data);
console.log(data);
console.log(data);
})
运行结果如下:

上面只是利用了Promise中的resolve函数,但是什么时候会利用到后面的reject函数呢?
实际上第一个参数resolve是映射到成功执行的内容,而第二个参数reject则是执行失败的内容。
new Promise((resolve,reject)=>{
setTimeout(() => {
// 成功的时候调用resolve,并执行后面的then()内容
// resolve中传什么,后面的then中就传什么
// resolve('Hello World')
// 失败的时候调用reject,并执行后面的catch()内容
reject('error message')
}, 1000);
}).then((data)=>{
//成功处理代码
console.log(data);
console.log(data);
console.log(data);
console.log(data);
}).catch((err)=>{
// 失败处理代码
console.log(err);
})
执行结果如下:
因此可以得出如此的思路图
=>{
setTimeout(() => {
// resolve('Hello vuejs')
reject('error message')
}, 1000);
// 拒接和满足都写在then中,即then中包含两个参数
}).then(data=>{
console.log(data);
},err=>{
console.log(err);
})
</script>
Promise的链式调用简写
需求如下:
// 网络请求:aaa -> 自己处理10行
// 处理 aaa111 ->自己处理10行
// 处理 aaa111222 ->自己处理10行
①
<script>
// 网络请求:aaa -> 自己处理10行
// 处理 aaa111 ->自己处理10行
// 处理 aaa111222 ->自己处理10行
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('aaa')
}, 1000);
}).then((res)=>{
// 自己处理10行
console.log(res,'第一层的十行处理代码');
// 2.
return new Promise((resolve)=>{
resolve(resolve+'111')
}).then(res=>{
console.log(res,'第二层的10行处理代码');
return new Promise((resolve)=>{
resolve(res+'222')
}).then(res=>{
console.log(res,'第三层的10行处理代码');
})
})
})
</script>
我们可以看到,这个代码块中,只有第一层是用了异步的,其他的时候都没有用异步。
那有没有针对于这种情况下的写法呢?
②省略掉new Promise()
写法如下:
即 不用再new一个 直接用Promise.resolve()来进行处理
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000);
}).then(res => {
// 自己处理10行
console.log(res, '第一层的十行处理代码');
// 2.
return Promise.resolve(res + '111')
}).then(res => {
console.log(res, '第二层的10行处理代码');
return Promise.resolve(res + '222')
}).then(res => {
console.log(res, '第三层的10行处理代码');
})
执行结果如下:
但是依旧有些冗余,因此继续简化。
③省略掉Promise.resolve(),直接return
之所以可以这么做是因为,在直接return时,它内部会自动对这个内容进行Promise包装。
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000);
}).then(res => {
// 自己处理10行
console.log(res, '第一层的十行处理代码');
// 2.
return (res + '111')
}).then(res => {
console.log(res, '第二层的10行处理代码');
return (res + '222')
}).then(res => {
console.log(res, '第三层的10行处理代码');
})
④若是reject,简写方法步骤雷同
(也可以用抛出异常 throw 效果相同)
在这里插入代码片// 失败
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000);
}).then(res => {
// 自己处理10行
console.log(res, '第一层的十行处理代码');
// 用reject或者抛出异常throw 'error message'都可以
// return Promise.reject('error message')
throw 'error message'
}).then(res => {
console.log(res, '第二层的10行处理代码');
return Promise.resolve(res + '222')
}).catch(err => {
console.log(err);
})
执行结果如下:
若多个请求完成后才能返回
利用:Promise.all(迭代器).then(结果数组)
注:这里主要是理解这种思路
①
<script>
// 若某个的返回条件是 需要多个请求结果
// all中的参数是一个数组,数组中放了多个请求
Promise.all([
new Promise((resolve,reject)=>{
$ajax({
url:'url1',
success:function(data){
resolve(data)
}
})
}),
new Promise((resolve,reject)=>{
$ajax({
url:'url2',
success:function(data){
resolve(data)
}
})
})
// results为数组,分别保存从上到下的各个请求
]).then(results =>{
// 这里是不能运行的,因为没有引用jq,因此并没有url,因此我们还是用settimeout来进行模拟
})
</script>
②用SetTimeout进行模拟
Promise.all([
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({
name:'yzk',age:20})
}, 1000);
}),
new Promise((resolve,reject)=>{
setTimeout(() => {
resolve({
name:'Kobe',age:21})
}, 2000);
})
]).then(results =>{
console.log(results);
})
运行结果如下: