一、前言⚔️
昨天已经讲过了异步和JS的执行机制了,异步是一种操作模式,有着执行的非阻塞性和顺序的非确定性。JS的执行机制就是Event Loop那一套流程了。
异步使得JS不会是一个愣头青一般的语言,不会死死等待耗时代码先执行,大大提升了效率。可是代码的执行顺序和编写顺序可能不一样,那我们又要如何解决这一问题呢?
二、前人操作-回调
看一下下面的常见情况:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
javascript
var data = null
function a() { // ajax
setTimeout(() => {
data = 'Hello'
}, 1000)
}
function b() {
console.log(data);
}
a()
b()
假设这里的function a
是一个网络请求,需要花费时间才能请求到数据,接着在function b
中打印数据。很明显,如果正常执行代码,最终打印的data
只会是null
,而得不到'Hello'
,JS执行可不会等你慢慢拿到数据后在执行这些同步代码。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
有人会说将function b
也设置成一个异步函数不就行了吗,不会真有人这么想的吧?因为虽然我们这里是明确了function a
的异步时间,但是现实情况中时间是不确定的,我们不可能设置一个巨长的时间来确保function a
执行早于function b
吧。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
所以这样一个让人头疼的问题以前的大佬们又是怎么解决的呢?文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
javascript
var data = null
function a() { // ajax
setTimeout(() => {
data = 'Hello'
b() // <--------
}, 1000)
}
function b() {
console.log(data);
}
a()
直接将function b的调用写在function a的逻辑体内,无论你function a要多久才能执行,我function b都紧紧跟着,要等那就一起等,你执行了我才执行。这样一看,好像问题是解决了吧,是解决了,并且看起来十分的优雅。
但通常会换一个写法:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
javascript
var data = null
function a(callback) { // ajax
setTimeout(() => {
data = 'Hello'
callback() // <--------
}, 1000)
}
function b() {
console.log(data);
}
a(b)
将function b
作为参数传给function a
,等到a
里的逻辑执行完毕,再来执行作为一个回调的function b
。这种情况代码看起来很优雅,也就是说还有别的情况这样做并不行。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
我现在的需求是,d
的执行依赖于c
的结果,c
的执行依赖于b
的结果...
按照上面的思路,我们的代码就得这样写:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
scss
function a(cbB, cbC, cbD) {
cbB(cbC, cbD)
}
function b(cbC, cbD) {
cbC(cbD)
}
function c(cbD) {
cbD()
}
function d() {
}
a(b, c, d)
将b
作为参数传入a
,将c
作为参数传入b
...当a
执行的完的时候,要保证b
执行完的时候执行c
,所以要将c作为参数一起传入a
...
看到这段代码,人都麻了。要是开发中真的这样写代码,怎么来的怎么回去,简直就是回调地狱。嵌套过深,一旦出现问题,很难排查,维护难度太大。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
这样的操作没人受得了,难免会有人去JS官方轰炸。最终官方也受不了,被diss了那么久,也决定关于这方面优化一下,让大家编程时更轻松。于是官方在2015年打造了promise。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
三、给一个承诺-promsie
promise妥妥的就是为了帮助我们解决异步这一问题。一起来看看什么是promise。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/16396.html
1、烧水与泡茶
scss
function boiling() { // pending resolved rejected
return new Promise((resolve, reject) => { // {status: pending}
setTimeout(() => {
console.log('正在烧水中');
resolve()
}, 2000)
})
}
function tea() {
setTimeout(() => {
console.log('正在泡茶中');
}, 1000)
}
boiling()
.then(() => {
tea()
})
烧水泡茶的时候,我们需要等待水烧开后,才可以开始泡茶。如果什么都不做,因为异步的时间不同,所以打印的会先是'正在泡茶中'
,再是'正在烧水中'
。这明显不合乎常理,所以使用promise。
new
一个Promise
实例,接收两个函数作为参数。
当调用boiling()
时,它返回一个处于pending
(进行中)状态的承诺,意味着烧水已经开始但尚未完成。通过setTimeout
模拟了烧水需要的时间,2秒后,控制台会打印出"正在烧水中",然后调用resolve()
(已解决)方法,将Promise的状态从pending
变为resolved
,表示烧水完成。
当boiling()
中的Promise变为resolved状态时,紧随其后的.then()
方法里的回调函数会被执行。这里的回调函数内容是调用tea()
函数,意味着一旦烧水完成,立即开始进行泡茶的操作。
这样就做到了水没有烧开,那就泡不了茶,一旦水烧开就可以立即泡茶。这里不用太纠结pending
和resolved
到底是什么,这是JS官方打造的一个词,表示是什么什么状态。也可以直接去MDN查看。
2、接着开始喝茶
茶都泡好了,那就开始喝茶了。
javascript
function boiling() { // pending resolved rejected
return new Promise((resolve, reject) => { // {status: pending}
setTimeout(() => {
console.log('正在烧水中');
resolve()
}, 2000)
})
}
function tea() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('正在泡茶中');
resolve()
}, 1000)
})
}
function drink() {
console.log('开始喝茶');
}
boiling().then(() => {
tea.then(() => {
drink()
})
})
在这个情况下,发现.then
怎么又开始嵌套了呢?于是官方进行再更新,使得.then
可以跟在.then
后面。
scss
boiling()
.then(() => {
tea()
})
.then(() => {
drink()
})
但是运行一遍会发现,打印的结果并不是我们期望的。
这是为什么呢?明明第二个.then
是跟在你第一个.then
后面的呀。这是因为then
方法支持链式调用,虽然then
默认也会返回一个promise实例对象,但是状态默认是pending
, 这就会导致后面的then
用不上前面then
的状态,从而继续往前查找。
scss
boiling()
.then(() => {
return tea()
})
.then(() => {
drink()
})
要解决问题可以自己添加一个return
来覆盖then
自带的返回。最终,艺术达成!
四、结语?
到此为止,今天的分享就结束了。希望看完对你有一点帮助,一起加油!
评论