js入门指南之Promise:从''承诺''到理解,告别回调地域

在了解promise之前,我们需要简单了解一些关于异步的知识,方便解释promise具体做了什么,起到什么样的作用

一、异步

  1. js在设计之初时被作为浏览器脚本语言来设计,所以被设计成了单线程以节省用户的性能

  2. js代码中如果存在需要耗时执行的代码(如setTimeout()),则该代码在v8引擎执行到这时,会被暂时挂起,而优先执行后面不耗时的代码(同步代码),这就是js中异步的简单理解

3.通过一段代码直观了解异步

javascript 复制代码
let a = 1
let b = 2
setTimeout(() => {

console.log(a)

},1000)
console.log(b)
   
   
   输出结果为2,一秒后再输出1

所以,如果你不了解什么是异步,那么你是否会觉得是函数在读取到第四行代码后,等待一秒后打印1再打印2?

二、如何处理异步

通过上个标题内容,你是否了解到了在v8中引擎执行的顺序? 那么,js中异步这一种情况会给我们带来什么样的问题与困境呢,不急,请看如下情形中的代码

scss 复制代码
let  a = 1

function fn(){
    setTimeout(()=>{
    a = 2
      console.log(a)    
    },1000)
   }

function foo(){
     setTimeout(()=>{
     a = 3
    console.log(a)  
    },2000)
}

function bar(){
console.log(a)
   }

foo()
fn()
bar()

此时由于异步的存在,你会发现,无论你如何调用foo(),fn(),bar(),都是以bar(),fn(),foo()这样的顺序依次打印出来,使得你无法控制他们的执行顺序,那么此时我们该如何解决这个问题呢

1、解决方法

(1)通过函数嵌套回调调用,改变执行顺序(容易形成屎山代码,不推荐)

此时你想到一个办法,我通过将下一个执行的函数调用于上一个执行函数的末尾,不就能解决异步的问题了吗,通过这个手段就可以实现执行顺序为bar(),fn(),bar()了,于是你写出了这样的代码

scss 复制代码
let  a = 1

function fn(){
    setTimeout(()=>{
    a = 2
      console.log(a)    
    },1000)
    bar()
   }

function foo(){
     setTimeout(()=>{
     a = 3
    console.log(a)
    fn()
    },2000)
}

function bar(){
console.log(a)
   }
   
   foo()
缺陷

此时你便成功实现了执行顺序为bar(),fn(),bar(),但是,这又引发了另一个问题,仔细思考,这里是三个函数的回调,

但是你要是有大量的函数需要回调呢?此时你会发现虽然代码依然可以运行,且按照了你预想的顺序执行,但是,一旦出现了bug,你便会发现一件事,那就是你只能在该回调函数中一个一个寻找错误点,使得项目代码难以阅读和维护让人十分难以解决该bug,这就是屎山代码的样子

大量函数代码的回调形成的维护困难这一情况,也称之为回调地域,我们在平时解决异步问题时,要避免造成这种情况

(2)通过使用Promise的链式调用,解决异步问题

虽然函数嵌套回调调用可以解决问题,但是带来的代码难以阅读与维护,寻找错误困难的问题 也不是我们希望能看见的,所以,我们此时应用Promise这一函数来解决异步的问题

Promise的救赎

当你第一次见到Promise,你或许会想,这不是承诺的意思吗?什么是承诺?

我们继续通过代码来使得更好的理解

javascript 复制代码
function gohome(){
 setTimeout(() => {
    console.log('成功回家')
 
 },4000)
     }
function  shower(){
    setTimeout(() => {
     console.log('你洗澡了')
    },1000)

   }
function gosleep(){
    setTimeout(() => {
    console.log('你上床休息了')
    },2000)

}

此时由于三个函数均要消耗时间,且根据所需消耗时间来判断,无论什么顺序调用函数,都是以shower,gosleep,gohome的顺序执行的,此时我们便需要使用Promise函数来解决这个问题了

表达式一
scss 复制代码
function gohome() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('成功回家')
            resolve()
        }, 4000)
           })
   
}

function shower() {
 return new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('你洗澡了')
        resolve()
    }, 1000)
  })
}
function gosleep() {
    setTimeout(() => {
        console.log('你上床休息了')
    }, 2000)

}

gohome().then(() => {
    shower().then(() => {
     gosleep()
    })
})

运行以上代码,你就能发现,问题解决,使得函数构成了链式的调用,且使得代码更扁平,更易管理

除了上面代码这种方法,promise还有另一种使用方式,能使代码更简洁明了,更加直观

表达式二
javascript 复制代码
function gohome() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('成功回家')
            resolve()
        }, 4000)
        })
   
}

function shower() {
 return new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('你洗澡了')
        resolve()
    }, 1000)
  })
}
function gosleep() {
    setTimeout(() => {
        console.log('你上床休息了')
    }, 2000)

}

gohome().then(() => {
    return shower()
})
.then(() =>{
    gosleep()
})
详细说明
  1. 使用了Promise函数后,原函数获得一个实例对象,只有由Promise构造出的实例对象后面才能接then()

  2. Promise函数要传入一个函数,而传入的这个函数还需要接受两个参数(resolve,reject),分别需要再Promise函数中调用,用于表示成功与失败若读取到成功,则立即执行其实例对象所接到的then()内部的函数,若失败则不执行

  3. then()只能被由Promise函数创建的实例对象调用,原因是then()存放在Promise()的显示原型上,而其 实例对象的隐式原型继承了其构造函数的显示原型,then()内部要传入一个函数,当其得到Promise()中resolve()被执行的信息后,会立即执行then()内部传入的函数 (若对原型有疑问,参考前文深入浅出:理解js的'万物皆对象'与原型链)

4.在表达式二中,该表达方法中的第一个then()会默认继承上Promise内读取到的resolve或reject

  1. 在.then中返回的值(无论是普通值还是新的Promise),都会成为下一个.then接受的参数,所以为了在使用表达式二中,避免后续的then全部默认接受同一个参数,记得为每一个then添加一个返回值,避免出现''漂浮的Promise''
相关推荐
YaeZed2 小时前
Vue3-watchEffect
前端·vue.js
boombb2 小时前
H5 图片路径不统一,导致线上部分图片无法按预期展示(assetPrefix 与 basePath 行为不一致)
前端
栀秋6662 小时前
深入浅出AI流式输出:从原理到Vue实战实现
前端·vue.js·前端框架
柳成荫2 小时前
Chromium 渲染机制
前端
UIUV2 小时前
JavaScript流式输出技术详解与实践
前端·javascript·代码规范
weixin_462446232 小时前
PyQt 与 Flask 融合:实现桌面端一键启动/关闭 Web 服务的应用
前端·flask·pyqt
Hy行者勇哥2 小时前
Edge 网页长截图 + 网站安装为应用 完整技术攻略*@
前端·edge
ujainu2 小时前
Flutter入门:Dart基础与核心组件速成
javascript·flutter·typescript
Dreamboat-L2 小时前
VUE使用前提:安装环境(Node.js)
前端·vue.js·node.js