给我一个promise,就跟着你了

零 JavaScript教程评论74字数 2990阅读9分58秒阅读模式

给我一个promise,就跟着你了

一、前言⚔️

昨天已经讲过了异步和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()函数,意味着一旦烧水完成,立即开始进行泡茶的操作。

这样就做到了水没有烧开,那就泡不了茶,一旦水烧开就可以立即泡茶。这里不用太纠结pendingresolved到底是什么,这是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()
})

但是运行一遍会发现,打印的结果并不是我们期望的。

image.png

这是为什么呢?明明第二个.then是跟在你第一个.then后面的呀。这是因为then方法支持链式调用,虽然then默认也会返回一个promise实例对象,但是状态默认是pending, 这就会导致后面的then用不上前面then的状态,从而继续往前查找。

scss

复制代码
boiling()
.then(() => {
    return tea()
})
.then(() => {
    drink()
})

要解决问题可以自己添加一个return来覆盖then自带的返回。最终,艺术达成!

四、结语?

到此为止,今天的分享就结束了。希望看完对你有一点帮助,一起加油!

零
  • 转载请务必保留本文链接:https://www.0s52.com/bcjc/javascriptjc/16396.html
    本社区资源仅供用于学习和交流,请勿用于商业用途
    未经允许不得进行转载/复制/分享

发表评论