前言
今天让我们的学习更进一步,来聊一聊在ES6中JS中新增的内容promise,我们在英语中的promise时承诺的意思,那在js中promise是什么呢?又会是怎么使用的呢?
异步
在我们介绍promise之前我们先来聊一聊一些基本的概念,我们学习过Java的同学们我们都知道,代码都是从上到下来执行的,但在代码执行中我们知道有些代码执行是要耗时的,有的不用耗时,在我们编程中把不耗时代码叫同步代码,耗时的代码叫异步代码。那我们在js中遇到了耗时的代码在执行过程中输入和操作的呢?让我们通过定时器这一个耗时代码示例来看一下
js
let a = 1
console.log(a,2);
setTimeout(()=>{
a = 2
console.log(a,8);
},1000)
;
我们通过js中的定时器来实现等待一秒钟来进行内部操作那我们在上面的代码运行就是先打印1,2,等待一秒后再去打印2,8
我们来把这份代码改一改来理解异步
js
let a = 1
console.log(a,2);
setTimeout(()=>{
a = 2
console.log(a,8);
},1000)
console.log(a,12);
我们在定时器后再去加一个console.log(a,12);那我们是先等一秒执行好定时器内容后再去执行打印a,12,还是去执行打印最后在执行定时器呢》我们来看一下代码的运行
我们发现在执行的过程中先去执行了打印a,12,后再执行定时器的内容,此时我们打代码并没有按照从上到下顺序来进行,此时就是异步了。
我们所学习的js是单线程语言,我们来想如果我们没有异步操作,那我们在执行代码中我们每等到好事代码执行完,再才能去执行后面代码的执行方案,那代码执行效率是不是就会大大降低。所以当我们在js中当我们遇到需要耗时执行的代码就将其先挂起,等到后续不耗时的代码执行完毕后再回过头来执行耗时的代码
1.js是单线程语言:一次只能干一件事情 (多线程执行效率快,但是开销性能很多)
2.js遇到需要耗时执行的代码就将其先挂起,等到后续不耗时的代码执行完毕后再回过头来执行耗时的代码
解决异步
我们发现在js中我们遇到耗时的代码时会出现异步现象,但我们在实际编程操作时有时希望不去产生异步,就要当还是代码运行结束后,使用耗时代码内部数据来进行下面的操作,那我们如何去解决异步问题?
1.回调
回调是我们js中为了解决异步问题最早使用的方法,下面让我们根据代码来看这个个方法如何去使用
js
function a(){
setTimeout(()=>{
console.log('a执行完毕');
// b()
cb()
},1000)
}
function b(){
console.log('b执行完毕');
}
a()
b()
我们上面这份代码,函数a里我们放入一个定时器来模拟要进行耗时的操作,当函数a被调用那么一秒钟后才会进行函数a中的操作,我们在定义一个函数b,当我们调用函数a和b后我们先执行函数b等一秒后执行函数a 那我们如何通过回调来使我们能够先去执行函数a当函数a执行完后再去执行函数b呢?
js
function a(){
setTimeout(()=>{
console.log('a执行完毕');
b()
},1000)
}
function b(){
console.log('b执行完毕');
}
a()
我们能够将函数b的调用放在函数a内,当函数a中的定时器执行结束再去调用函数b实现。我们为了将代码写的更为优雅一般都会将上面代码优化为如下写法
js
function a(cb){
setTimeout(()=>{
console.log('a执行完毕');
cb()
},1000)
}
function b(){
console.log('b执行完毕');
}
a(b)
我们在函数a中放入一个形参cb在调用函数a时给它传入实参函数b并在函数中定时器结束后调用函数b.
在我们使用这个方法时如果在我们在日后实际项目处理中,代码过多,我们每遇到了耗时函数我们通过回调来去解决这个个问题是会出现回调地狱(代码嵌套过深,维护成本过大,一旦出现了问题很难排查)
2.promise
我们程序员在经历漫长时间的回调地狱去解决异步,在不断催促官方后,官方终于在ES6的版本更新后增加了我们的主角promise来更优雅的去解决异步的问题让我们通过代码来去看聊一聊这个个方法是如何去使用的。
我们来一个相亲的函数来模拟小明的相亲过程
js
function xq() {
setTimeout(() => {
console.log('小明相亲了');
}, 2000)
}
function marry() {
console.log('小明结婚了');
}
xq()
marry()
我们输出发现代码出现了异步现象,小明还没相亲就结婚了,这起不毁了吗?我们固有认知都是先相亲再结婚的吧,那就让我们通过使用promise方法改一改代码解决异步来操控小明。
js
function xq() {
//resolve是执行成功的意思,reject是执行失败的意思
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('小明相亲了');
resolve('相亲成功了')
}, 2000)
})
}
function marry() {
console.log('小明结婚了');
}
xq().then((res) => {
console.log(res)
marry()
})
我们通过上面的使用promiose方法来实现对小明的操作,完成了他的相亲,和结婚操作,我们观察promise方法不难看出,这个优雅的方法并不像回调不断地嵌套,套娃这么操作,只需要将异步的地方返回一个Promise的实例对象,并且传入一个回调函数,其中有两个参数
Promise两个参数:
- resolve:执行成功后执行then()操作,可以传入参数并将传入的值返回给then中的回调函数
- reject:执行失败后执行catch()操作,并抛出错误
然后我们在then中传入一个回调函数,并且用res
接收前面resolve传入过来的参数,输出后调用marry()
,使用Promise的过程我们用咱自己的话来说:咱如果想让它按顺序执行就是让他听话,我们先给它一个承诺Promise
如果下定了决心resolve
实现这个承诺,然后then
就能让它就能明白你的决心res
按照你接下来想做的事情执行。 如果没有下定决心来实现这个承诺它就得拒绝reject
,然后就它就不会帮你干这事情并且让你认识到这个错误catch
。
多层回调
我们通过上面代码实现简单的一次回调的问题,但当我们进行多层回调,出现回调地狱时,我们又该使用promise方法如何使用?
我们接着以上面的示例为例继续操作小明的人生。小明结婚成功以后是不是就会有小小明的出生。
js
function xq() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('小明相亲了');
resolve('相亲成功了')//只能放在定时器里面
}, 2000)
})
}
function marry() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('小明结婚了');
resolve('成功结婚了')
}, 1000)
})
}
function baby() {
console.log('小小明出生了');
}
xq().then((res) => {
console.log(res);
marry().then(() => {
baby()
})
})
我们可以看到在第一层中我们在调用完了xq
之后使用then
进行接下来的marry
,然后在marry
中再次使用了Promise
,然后在marry执行完后再执行then中的baby
。如果有多个的话我们就可以一直这样嵌套下去,这样的话就提高了代码的复用性,如果有修改函数调用就不用去函数体中修改了。这时候就有同学会问了,有没有更优雅的方法,下面我们来看看使用Promise实现的另一种样子:
js
//前面三个函数还是一样的,下面我们来对then部分进行修改
xq()
.then((res) => {
console.log(res);
return marry()//返回的是Promise实例,所以可以继续then
})
.then((res) => {
console.log(res);
baby()
})
我们将上面代码拆解开来,由于marry()
返回的是一个Promise实例对象,所以我们可以对其接着用then
进行调用,所以以后在写代码时如果碰到了多重嵌套的话就可以使用这种方法方便我们查看嵌套。
实际应用
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
button {
width: 100px;
height: 30px;
}
</style>
</head>
<body>
<button>请求数据</button>
<ul id="ul"></ul>
<script>
let data={}
function getData(){
return new Promise((resolve,reject)=>{
let xhr = new XMLHttpRequest()
xhr.open('GET', 'https://mock.mengxuegu.com/mock/65a91543c4cd67421b34c898/movie/movieList', true)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(JSON.parse(xhr.responseText));
resolve(JSON.parse(xhr.responseText).movieList)
}
}
})
}
function showList(data){
data.forEach(item => {
let li = document.createElement('li')
li.innerText = item.nm
document.getElementById('ul').appendChild(li)
});
// console.log(`拿到了数据$(data)`);
// console.log(data);
}
getData().then((res)=>{
showList(res)
})
</script>
</body>
</html>