Promise

区别实例对象与函数对象

  1、实例对象:new 函数产生的对象,称为实例对象,简称对象

  2、函数对象:将函数作为对象使用时,简称函数对象

<script>
    function Fn(){//Fn函数
    }
    const fn = new Fn() //Fn是构造函数,fn是实例对象(简称对象)
    console.log(Fn.prototype)//Fn是函数对象
    Fn.bind({})//调用函数对象的bind方法
    $("#test") //jQuery函数
    $.get("/test") //jQuery函数对象,括号左边是函数,点的左边是对象
  </script>

两种类型的回调函数

  回调函数:回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调

  同步回调

    理解:立即执行,完全执行完了才结束,不会放入到回调队列中

    例子:数组遍历相关的回调函数,Promise的excutor函数

  异步回调

    理解:不会立即执行,会放入到队列中将来执行

    例子:定时器回调、ajax、Promise的成功|失败的回调

<script>
    //1、同步回调函数
    const arr = [1,2,3]
    arr.forEach((item) =>{//遍历回调函数,同步回调函数,不会放入队列,一上来就要执行完
        console.log(item)
    })
    console.log("forEach函数之后")
    //2、异步回调函数
    setTimeout(() => {//异步回调函数,会放入队列中将来执行
        console.log("timeout callback()")
    }, 0)
    console.log("setTimeout函数之后")//先于setTimeout的回调函数执行
</script>

JS中的error处理

  1、错误的类型

    1、Error:所有错误的父类型

    2、ReferenceError:引用的变量不存在

    3、TypeError:数据类型不正确的错误

    4、RangeError:数据值不再其允许的范围内

    5、SyntaxError:语法错误

  2、错误处理

    1、捕获错误:try ... catch

    2、抛出错误:throw error

  3、错误对象

    message属性:错误相关信息

    stack属性:函数调用栈记录信息

<script>
    //1、常见的内置错误
    //console.log(aaa) //ReferenceError: aaa is not defined
    console.log("----")//没有捕获error,下面的代码不会执行
    let b = null
    //console.log(b.xx) //TypeError: b is null
    // function fn(){
    //     fn()
    // }
    // fn() //RangeError: Maximum call stack size exceeded
    const c = """" //SyntaxError: unexpected token: string literal

  //2、错误处理
    try{
        let d
        console.log(d.xxx)
    }catch(error){
        console.log(error.message)//d is undefined
        console.log(error.stack)// @file:///home/xdl/python/web/day01/code/01_html.html:71:3
    }
    console.log("出错之后")//可以正常执行
    //抛出异常
    function something(){
        if(Date.now()%2===1){
            console.log("当前时间为奇数,可以执行任务")
        }else{//如果时间为偶数抛出异常,由调用者来处理
            throw new Error("当前时间为偶数,无法执行任务")
        }
    }
    try{
        something()
    }catch(error){
        console.log(error.message)//当前时间为偶数,无法执行任务
    }
</script>

promise的理解和使用

  1、promise是什么?

    理解:

      1、抽象表达:promise是JS中进行异步编程的新的解决方案(之前是纯回调的方式)

      2、具体表达:

        1、从语法上来说:promise是一个构造函数

        2、从功能上来说:promise对象用来封装一个异步操作并可以获取其结果

    promise的状态改变:

      1、pedding变为resolved

      2、pedding变为rejected

      说明:只有这两种,且一个promise对象只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为value,失败的结果数据一般称为reason

    promise的基本使用

<script>
    //1、创建一个新的promise对象
    const p = new Promise((resolve, reject) => {//执行器函数,同步回调
        //2、执行异步任务
        setTimeout(() => {
            const time = Date.now() //如果当前时间是偶数代表成功,否则代表失败
            if (time % 2 === 0){
                //3.1、如果成功,调用resolve(value)
                resolve("成功的数据,time=" + time)
            }else{
                //3.2、如果失败,调用reject(reason)
                reject("失败的数据,time=" + time)
            }  
        },1000)
    })
    p.then(
        (value) => {//接收得到成功的value数据,onResolved
            console.log("成功的回调", value)
        },
        (reason) => {//接收得到失败的reason数据, onRejected
            console.log("失败的回调", reason)
        }
    )
</script>   

  2、为什么要使用promise

     1、指定回调函数的方式更加灵活:

      旧的:必须在启动异步任务前指定

      promise:启动异步任务 => 返回promise对象=>给promise对象绑定回调函数(甚至可以在异步执行完成之后)

    2、支持链式调用,可以解决回调地狱问题

      什么是地狱问题?回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件

      回调地狱的缺点?不便于阅读,不便于异常处理

      解决方案?promise链式调用

      终极解决方案?async、await

  3、如何使用promise

    API

      1、Promise构造函数:Promise(excutor){}

        excutor函数:同步执行(resolve, reject)=> {}

        reslove函数:内部定义成功时我们调用的函数value=>{}

        reject函数:内部定义失败时我们调用的函数 reason=>{}

        说明:excutor会在promise内部立即同步回调,异步操作在执行器中执行

      2、Promise.prototype.then方法:(onResolved, onRejected)=>{}

        onResolved函数:成功的回调函数 (value)=>{}

        onRejected函数:失败的回调函数(reason)=>{}

        说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,返回一个新的promise对象

      3、Promise.prototype.catch方法:(onRejected)=>{}

        onRejected函数:失败的回调函数(reason)=>{}

        说明:then()的语法糖,相当于:then(undefined,onRejected)

new Promise((resolve, reject) =>{
        setTimeout(()=>{
            resolve("成功的数据")
            //reject("失败的数据") // 只会执行第一个
        }, 1000)
    }).then(
        (value) => {
            console.log("onReaolved()1",value)
        }
    ).catch(
        (reason) => {
            console.log("onRejected()1", reason)
        }
    )

      4、Promise.resolve方法:(value)=> {}

        value:成功的数据或promise对象

        说明:返回一个成功或失败的promise对象

      5、Promise.reject方法:(reason)=>{}

        reason:失败原因

        说明:返回一个失败的promise对象

  /产生一个成功值为1的promise对象
    const p1 = new Promise((resolve, reject)=>{
        resolve(1)
    })
    const p2 = Promise.resolve(2)
    const p3 = Promise.reject(3)
    p1.then(value=>{console.log(value)})
    p2.then(value=>{console.log(value)})
    p3.then( null, reason=>{console.log(reason)})
    p3.catch(reason=>{console.log(reason)})

      6、Promise.all方法:(promise)=>{}

        promise:包含n个promise的数组

        说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败了就直接失败

const p1 = new Promise((resolve, reject)=>{
        resolve(1)
    })
    const p2 = Promise.resolve(2)
    const p3 = Promise.reject(3)
    const pAll = Promise.all([p1,p2,p3])
    pAll.then(
        values => {
            console.log("all onResolved()", values)//如果成功将返回一个数组
        },
        reason =>{
            console.log("all onRejected()", reason)//3
        }
    )

      7、Promise.race方法:(promise)=>{}

        promise:包含n个promise的数组

        说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态,按照数组的顺序开始执行异步函数 

//产生一个成功值为1的promise对象
    const p1 = new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve(1)
        })
    })
    const p2 = Promise.resolve(2)
    const p3 = Promise.reject(3)
    const pRace = Promise.race([p1,p2,p3])
    pRace.then(
        value => {
            console.log("race onResolved()", value)
        },
        reason =>{
            console.log("race onRejected()", reason)
        }
    )

promise的关键问题

  1、如何改变Promise的状态

    1、resolve(value):如果当前是pedding就会变为resolved

    2、reject(reason):如果当前是pedding就会变为rejected

    3、抛出异常:如果当前状态是pedding就会变为rejeceted

const p = new Promise((resolve, reject) =>{
        //resolve(1)//promies变为resolved成功状态
        //reject(2)//promise变为rejected失败状态
        //throw new Error("出错了")//抛出异常promise变为rejected失败状态,reason 为抛出的error
        throw 3
    });
    p.then(
        reason => {console.log("reason:", reason)}//3
    )

  2、一个promise指定多个成功或失败回调函数,都会调用吗?

    当promise改变为对应状态时都会调用

const p = new Promise((resolve, reject) =>{
        resolve(1)
        
    });
    p.then(
        value => {console.log("value1:", value)}//value1: 1
    )
    p.then(
        value => {console.log("value2:", value)}//value2: 1
    )

  3、改变promise状态和指定回调函数谁先谁后?

    1、都有可能,正常情况下时先指定回调函数再改变状态,但也可以先改变状态再指定回调函数

    2、如何先改变状态再指定回调?

      1、在执行器中直接调用resolve()/reject()

      2、延迟更长时间才调用then()

//如何先改变状态,后指定回调函数
    new Promise((resolve, reject) =>{
        reject(1)//先改变的状态(同时指定数据)    
    }).then(//后指定回调函数,异步执行回调函数
        value => {console.log("value:", value)},
        reason => {console.log("reason:", reason)}
    )

    3、什么时候才能得到数据?

      1、如果先指定回调,那么状态发生改变时,回调函数就会调用,得到数据

      2、如果先改变的状态,那么指定回调时,回调函数就会调用,得到数据

new Promise((resolve, reject) =>{
        setTimeout(()=>{
            resolve(1)//后改变的状态(同时指定数据),异步执行回调函数
        }, 1000)    
    }).then(//先指定回调函数,保存当前指定的回调函数
        value => {console.log("value1:", value)}//value1: 1
    )

  4、promise.then()返回的新的promise的结果状态由什么决定?

    1、简单表达:由then()指定的回调函数执行的结果决定

    2、详细表达:

      1、如果抛出异常,新promise变为rejected,reason为抛出的异常

      2、如果返回的是非promise的任意值,新的promise变为resolved,value为返回的值

      3、如果返回的是另一个新promise,此promise的结果就会成为新promise的结果 

new Promise((resolve, reject) =>{
        resolve(1)
        //reject(1)
    }).then(
        value => {
            console.log("onResolved1():", value)
            //return 2
            //return Promise.resolve(3)
            //return Promise.reject(4)
            throw 5
        },
        reason => {
            console.log("onRejected1():", reason)
        }
    ).then(
        value => {
            console.log("onResolved2():", value)
        },
        reason => {
            console.log("onRejected2():", reason)
        }
    )

  5、promise如何串连多个操作任务

    1、promise的then()返回一个新的promise,可以看成then()的链式调用

    2、通过then的链式调用串连多个同步/异步任务

new Promise((resolve, reject) =>{
        setTimeout(()=>{
            console.log("执行任务1(异步)")
            resolve(1)
        },1000);
    }).then(
        value => {
            console.log("任务1的结果:", value)
            console.log("执行任务2(同步):")
            return 2
        }
    ).then(
        value => {
            console.log("任务2的结果():", value)
            return new Promise((resolve, reject)=>{
                setTimeout(()=>{
                    console.log("执行任务3(异步)")
                    resolve(3)
                },1000)
            })
        }
    ).then(
        value => {
            console.log("任务3的结果", value)
        }
    )

  6、promise异常传透?

    1、当使用promise的then链式调用时,可以在最后指定失败的回调

    2、前面任何操作出了异常,都会传到最后失败的回调中处理

new Promise((resolve, reject) =>{
        resolve(1)
        //reject(1)
    }).then(
        value => {
            console.log("onResolveed1():", value)
            return Promise.reject(2)
        }
    ).then(
        value => {
            console.log("onResolveed2():", value)
            return 3
        }
    ).then(
        value => {
            console.log("onResolveed3()", value)
        }
    ).catch(reason => {
        console.log("onRejected1()", reason)
    })

  7、中断promise链?

    1、当用promise的then的链式调用时,在中间中断,不再调用后面的回调函数

    2、办法:在回调函数中返回一个pedding状态的promise对象

new Promise((resolve, reject) =>{
        resolve(1)
        //reject(1)
    }).then(
        value => {
            console.log("onResolveed1():", value)
            return Promise.reject(2)
        }
    ).then(
        value => {
            console.log("onResolveed2():", value)
            return 3
        }
    ).then(
        value => {
            console.log("onResolveed3()", value)
        }
    ).catch(reason => {
        console.log("onRejected1()", reason)
        return new Promise(()=>{})//返回一个pedding状态的promise对象,中断promise链
    }).then(
        value => {
            console.log("onResolved4", value)
        },
        reason => {
            console.log("onRejected2()", reason)
        }
    )

async与await

  async函数

    1、函数的返回值为promise对象

    2、promise对象的结果由async函数执行的返回值决定

// async函数的返回值是一个promise对象
    //asyn函数返回的promise的结果由函数执行的结果决定
    async function fn1(){
        //return 1
        throw 2
    }
    const res = fn1()
    console.log(res)
    res.then(
        value =>{
            console.log("onResolved", value)
        },
        reason => {
            console.log("onRejected",reason)
        }
    )

  await表达式

    1、await右侧的表达式一般为promise对象,但也可以是其他的值 

     2、如果表达式是promise对象,await返回的是promise成功的值

    3、如果表达式是其他值,直接将此值作为await的返回值

function fn2(){
        return Promise.resolve(2)
    }
    async function fn3(){
        //const value = await fn2() // await右侧表达式为promise,得到的结果就是promise成功的value
        const value = await 6 //await右侧表达式不是Promis,得到的结果就是它本省
        console.log("value:",value)
    }
    fn3()

  注意:

    await必须写在async函数中,但是async函数中可以没有await

    如果await的promise失败了,就会抛出异常,需要通过try...catch来捕获处理

 JS异步之宏队列与微队列

  1、JS中用来存储执行回调函数的队列包含2个不同特定的队列

  2、宏队列:用来保存待执行的宏任务,比如:定时器回调,DOM事件回调,ajax回调

  3、微队列:用来保存待执行的微任务,比如:promise的回调,MutationObserver的回调

  4、JS执行时会区别这2两个队列

    1、JS引擎首先必须先执行所有的初始化同步任务代码

    2、每次准备取出第一个宏任务前,都要将所有的微任务一个一个取出来执行

应用:使用promise手动实现一个类似python中time.sleep()函数

// sleep 只能在async函数中使用
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
async function asyncFn() {
    await sleep(5000) // 等待5s
    console.log("等待异步函数执行结束")
} asyncFn() console.log(
"主进程不会阻塞先执行")

 

 

 

 

 

 

       

posted @ 2019-11-06 18:13  xdl_smile  阅读(247)  评论(0编辑  收藏  举报