promise详解[上]

摘要
本文是promise详解的[上]
第一章绪论
本章包括实例对象与函数对象的概念;同步异步概念;报错的类型和信息等

第二章promise的理解和使用
本章包括promise的基本流程;promise相比传统纯回调方式的优点;
promise的基本API使用;promise的几个关键问题(重难点!!!)

第一章 绪论

区别实例对象与函数对象

实例对象:new关键字产生的对象,称为实例对象,简称为对象

函数对象:函数是一种对象,将函数作为对象使用,称为函数对象

括号左边是函数
.左边是对象
函数也是一种对象

    function Fn() {   // Fn函数 
    }
    const fn = new Fn() // Fn是构造函数  fn是实例对象(简称为对象)
    console.log(Fn.prototype) // Fn是函数对象
    Fn.call({}) // Fn是函数对象
    $('#test') // jQuery函数
    $.get('/test') // jQuery函数对象

两种回调函数

自定义的函数,自己没有调用,最终执行的就是回调函数

同步回调

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

    // 1. 同步回调函数
    // 按顺序从上到下执行
    // const arr = [1, 3, 5]
    arr.forEach(item => { // 遍历回调, 同步回调函数, 不会放入列队, 一上来就要执行完
      console.log(item)
    })
    console.log('forEach()之后')

异步回调

不会立即执行 会放到回调队列中等待执行

    // 2. 异步回调函数
    setTimeout(() => { // 异步回调函数, 会放入队列中等待执行
      console.log('timout callback()')
    }, 0)
    console.log('setTimeout()之后')

错误处理

错误类型

Error 所有错误的父类型
ReferenceError 引用的变量不存在
TypeError 数据类型不正确的错误
RangeError 数据值不在允许的范围之内
SyntaxError 语法错误

错误不处理下面的代码不执行

  // ReferenceError: 引用的变量不存在
        // console.log(a) // ReferenceError: a is not defined

  // TypeError: 数据类型不正确的错误
        // let b
        // // console.log(b.xxx) // TypeError: Cannot read property 'xxx' of undefined
        // b = {}
        // b.xxx() // TypeError: b.xxx is not a function

  // RangeError: 数据值不在其所允许的范围内
        // function fn() {
        //   fn()
        // }
        // fn() // RangeError: Maximum call stack size exceeded
        // 超过能调用的次数限制

  // SyntaxError: 语法错误
        // const c = """" // SyntaxError: Unexpected string

错误处理

捕获错误
try … catch

try{
    let a;
    console.log(a.xxx);
}
catch(error){
    console.log(error);
}
// 这里的error是对象

抛出错误
throw error


  // 抛出错误: throw error
  function something() {
    if (Date.now()%2===1) {
      console.log('当前时间为奇数, 可以执行任务')
    } else { // 如果时间是偶数抛出异常, 由调用来处理
      throw new Error('当前时间为偶数无法执行任务')  
    }
  }

错误信息

message 错误信息
stack属性 函数调用栈记录信息


第二章 promise的理解和使用

promise是什么

是js中进行异步编程的新的解决方案(旧的是纯回调函数)

1、从语法上看promise是一个构造函数

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

状态改变

pending变为resolved

pending变为rejected

只有这两种,且一个promise对象只能改变一次
无论成功 还是失败,都会有一个结果数据

成功的结果数据一般称为value
失败的结果数据一般称为reason

基本流程

1、new Promise()
传一个函数作为参数,函数中启动异步任务
pending状态

2、执行异步操作
成功执行resolve() promise对象变为resolved状态
失败执行reject() promise对象变为rejected状态

3、.then
回调onResolve()

.then/catch回调onRejected()

4、返回一个新的promise对象

        // 1、创建新的promise
        const p = new Promise((resolve, reject) => {//执行器函数
            // 2、执行异步函数
            setTimeout(() => {
                const time = Date.now()// 如果当前时间是偶数代表成功,否则失败
                // 3.1成功 调用resolve(value)
                if (time % 2 === 0) {
                    resolve('成功的,value=' + time)
                }
                else {
                    // 3.2失败 调用reject(reason)
                    reject('失败的,reason='+ time)
                }

            }, 1000)
        })

        p.then(
            value => {
                // 接收得到的成功的value数据  onResolved
                console.log('成功的回调',value);
            },
            reason => {
                // 接收得到的失败的reason数据  onRejected
                console.log('失败的回调',reason);
            }
        )

为什么使用promise

纯回调形式的异步操作必须先指定回调函数再执行异步任务

promise执行器函数是同步回调,先执行异步任务,再指定回调函数

1、相对于纯回调函数指定回调函数的形式更加的灵活

灵活:
启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务得到结果之后再指定回调函数)

2、支持链式调用,解决回调地狱
什么是回调地狱?回调函数嵌套调用,涉及到多个异步操作,前后耦合性大,异常处理麻烦

回调地狱的最终解决方案async await

如何使用promise

API

  /* 
    1. Promise构造函数: Promise (excutor) {}
        excutor函数: 同步执行  (resolve, reject) => {}
        resolve函数: 内部定义成功时我们调用的函数 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)

    4. Promise.resolve方法: (value) => {}
        value: 成功的数据或promise对象
        说明: 返回一个成功/失败的promise对象

    5. Promise.reject方法: (reason) => {}
        reason: 失败的原因
        说明: 返回一个失败的promise对象

    6. Promise.all方法: (promises) => {}
        promises: 包含n个promise的数组
        说明: 返回一个新的promise, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败
    7. Promise.race方法: (promises) => {}
        promises: 包含n个promise的数组
        说明: 返回一个新的promise, 第一个完成的promise的结果状态就是最终的结果状态
  */
语法糖
    4. Promise.resolve方法: (value) => {}
        value: 成功的数据或promise对象
        说明: 返回一个成功/失败的promise对象



        const p1 = new Promise((resolve,reject) => {
            resolve(1)
        })

        // 等价于

        const p1 = Promise.resolve(1)

关键问题

  1. 如何改变promise的状态?
    (1)resolve(value): 如果当前是pendding就会变resolved
    (2)reject(reason): 如果当前是pendding就会变rejected
    (3)抛出异常: 如果当前是pendding就会变为rejected

        const p = new Promise((resolve, reject)=> {
        setTimeout(() => {
            // resolve('成功')
            // reject('失败的数据')
            throw 404
            // 抛出错误也会变为rejected状态
            // reason就是抛出的错误
        }, 2000)
    })
        p.then(
            reason => {
                console.log(reason);
            }
        )
    
  2. 一个promise指定多个成功/失败的回调函数都会调用吗?
    当pending状态改变为对应状态时都会调用
    ```js
    const p = new Promise((resolve, reject)=> {
    setTimeout(() => {
    // resolve(‘成功’)
    // reject(‘失败的数据’)
    throw 404
    // 抛出错误也会变为rejected状态
    // reason就是抛出的错误
    }, 2000)
    })
    p.then(
    reason => {
    console.log(reason);
    }
    )
    p.then(
    reason => {
    console.log(reason);
    }
    )

    
    
    
  3. 改变promise状态和指定回调函数谁先谁后?
    (1)都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
    (2)如何先改状态再指定回调?
    ①在执行器中直接调用resolve()/reject() (同步执行)
    ②延迟更长时间才调用then() (加定时器函数)
    (3)什么时候才能得到数据?
    ①如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
    ②如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据

  4. promise的执行器函数里面的函数(resolve()/reject())是同步执行的,
    .then本身是同步执行的,但是里面的成功/失败的处理回调函数都是异步执行的

         const p = new Promise((resolve, reject) => {
             console.log(11);
             resolve(1)
             console.log(22);
             reject(2)
         })
         p.then(
             // then本身是同步执行的
             // then里面的回调函数是异步的
             value => {
                 console.log(value);
             },
             reason => {
                 console.log(reason);
             }
         )
         console.log(0);
         // 11 22 0 1
    
  5. .then()会返回一个新的promise对象
    promise.then()返回的新promise的结果状态由什么决定?
    (1)简单表达: 由then()指定的回调函数执行的结果决定
    (2)详细表达:
    ①如果抛出异常, 新promise变为rejected, reason为抛出的异常
    ②如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值(例如返回数字)
    ③如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果

    new Promise((resolve, reject) => {
      resolve(1)
      reject(2)
    }).then(
      value => {// 此回调函数执行成功了 无返回值 默认返回undefined
        console.log('onResolved1()', value)
      },
      reason => {
        console.log('onRejected1()', reason)
      }
    ).then(
      value => {// 此回调函数根据上一个.then()返回的promise对象状态决定 执行成功 但是返回了一个undefined
        console.log('onResolved2()', value)
      },
      reason => {
        console.log('onRejected2()', reason)
      }
    )

    // 1  undefined
    new Promise((resolve, reject) => {
        // resolve(1)
        reject(2)
    }).then(
        value => {
            console.log('onResolved1()', value)
        },
        reason => {
            console.log('onRejected1()', reason)
            return 66
        }
    ).then(
        value => {
            console.log('onResolved2()', value)
        },
        reason => {
            console.log('onRejected2()', reason)
        }
    )
    // onRejected 2 onResolved 66
  1. promise如何串连多个操作任务?
    (1)promise的then()返回一个新的promise, 可以开成then()的链式调用
    (2)通过then的链式调用串连多个同步/异步任务

  2. promise异常传/穿透?
    (1)当使用promise的then链式调用时, 可以在最后指定失败的回调,(.catch())
    (2)前面任何操作出了异常, 都会传到最后失败的回调中处理

    new Promise((resolve, reject) => {
        // resolve(1)
        reject(1)
    }).then(
        value => {
            console.log('onResolved1()', value)
            return 2
        }
    ).then(
        value => {
            console.log('onResolved2()', value)
            return 3
        }
    ).then(
        value => {
            console.log('onResolved3()', value)
        }
    ).catch(
        reason => {
            console.log('onRejected',reason);
        }
    )

当前面的执行出现错误,不是从第一个promise一下子到最后的catch()中
会发生异常穿透
等价于:

    new Promise((resolve, reject) => {
        // resolve(1)
        reject(1)
    }).then(
        value => {
            console.log('onResolved1()', value)
            return 2
        },
        reason => {
            throw reason
        }
    ).then(
        value => {
            console.log('onResolved2()', value)
            return 3
        },
        reason => {
            throw reason
        }
    ).then(
        value => {
            console.log('onResolved3()', value)
        },
        reason => {
            throw reason
        }
    ).catch(
        reason => {
            console.log('onRejected', reason);
        }
    )

注意,除了throw reason 抛出异常
也等价于 reason => Promise.reject(reason)

箭头函数单条语句不加return,则默认返回单条语句
所以throw reason需要{},而reason => Promise.reject(reason)不需要
就是返回一个promise作为这一级.then()的返回对象

  1. 中断promise链
    (1)当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数
    (2)办法: 在回调函数中返回一个pendding状态的promise对象
    return new Promise(() => {})
    新的promise对象就是pending状态的对象
    从而实现终中断promise链条

上一篇:Promise学习笔记


下一篇:健康饮食是生产力的保证,教你如何健康饮食篇