Promise

218 阅读7分钟

整理 Promise 以及前端一些常用知识。

实例对象与函数对象的区别

  1. 实例对象:new函数产生的对象
  2. 函数对象:将函数作为对象使用时,称为函数对象。()左边是函数,.左边是函数对象

回调函数

回调函数特点:你声明的,没有调用,最终执行了。

  1. 同步回调:立即执行,完全执行完了才结束
  2. 异步回调:不会立即执行,会放入回调队列中将来执行 (定时器/ajax/promise)

JS 常见错误

  1. ReferenceError: 引用变量不存在
  2. TypeError: 数据类型不正确
  3. RangeError: 数据值不在允许的范围内
  4. SyntaxError: 语法错误

错误处理

  • 捕获错误: try ... catch, 在Promise执行器中用 try... catch,Promise外用Catch方法。因为try... catch 只能用于同步代码捕获。
try {
...
} catch(error) {
...
}

error包括 stack和message两个属性

  • 抛出错误: throw error

Promise

  • Promise是JS中进行异步编程的新的解决方案。

具体而言,从语法上说Promise是一个构造函数,从功能上来说Promise对象用于封装一个异步的操作并可以获取其结果。

Promise状态

1. pending变为resolved

2. pending变成rejected

一个Promise对象只能改变一次,成功的结果数据一般称为value,失败的结果数据一般称为reason。
  • Promise流程

    1. new Promise() 此时Promise处于pendding状态

    2. 执行异步操作,成功了执行resolve(),失败了执行reject()

    3. resolve() --> pendding变为resolved,rejected() --> pendding变为rejected

    4. resolved 执行通过.then()指定的回调 onResolved() 生成新的Promise对象,rejected 执行通过.then()/.catch()指定回调 onRejected() 通过.then()可以生成新的Promise对象。

  • 为什么用Promise

  1. 指定回调函数的方式更加灵活。以前异步操作的回调函数必须在启动异步任务前指定。Promise的回调函数甚至可以在异步结束后指定。

  2. 支持链式调用,可以解决回调地狱

回调地狱:回调函数嵌套调用,完成1后完成2....

Promise解决回调地狱:链式的.then

<script>
  dosomething().then(funciton(result){ //dosomething: Promise对象
    return dosomethingElse(result)
  })
  .then(funciton(newResult){
    return dosomeThirdThing(newResult)
  })
  .then(funciton(findResult){
    console.log('Got final result')
  })
  .catch(failureCallback)
</script>

async/awit:回调地狱的终极解决方案

async function request() {
  try {
    const result = await doSomething()
    const newResult = await doSomethingElse(result)
    const finalResult = await doThirdThing(newResult)
    console.log('Got final result')
  } catch (error) {
    failureCallback(error)
  }
}
  • Promise基本使用
<script>
    const p = new Promise((resolve, reject) => {
        // 执行函数 成功了条用resolve(value) 失败了调用 reject(value)
        setTimeout(()=>{
            const time = Date.now()
            if (time %2 ==0) {
                resolve("成功,time=" + time)
            } else {
                reject("失败,time=" + time)
            }
        },1000)
    })
    p.then(
        value => { console.log('成功的回调',value)},
        reason => { console.log('失败的回调',reason)
        }
    )
</script>
  • Promise API

    1. Promise构造函数:Promise(executor) {}

      • executor: 同步执行 (resolve,reject) => {}
      • resolve: 内部定义成功时我们调用的函数 value => {}
      • reject: 内部定义失败时我们调用的函数 reason => {}

      executor时同步执行,executor内代码是异步执行的

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

      • onResolved: 成功的回调 (value) => {}
      • onRejected: 失败的回调 (reason) => {} 返回一个新的Promise对象
    3. Promise.prototype.catch: (onRejected) => {}

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

      相当于 then(undefined, onRejected)

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

      value: 成功的数据或者Promise对象

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

      reason: 失败的原因

    6. Promise.all: (Promises) => {}

      Promises: 包含n个promise数组,返回一个新的Promise,只有所有的Promise都成功才成功,否则返回第一个的失败

    7. Promise.race: (Promises) => {}

      Promises: 包含n个promise数组,返回一个新的Promise,返回第一个完成的Promise结果

  • Promise的几个问题

    1. Promise状态改变:1) resolve(value): pendding -> resolved 2) reject(reason): pendding -> rejected 3) 抛出异常: pendding-> rejected

    2. Promise状态改变时,会调用所有指定的成功/失败的回调函数。

    3. 指定回调函数和改变状态的顺序,都有可能。默认改变状态和.then()是同步执行,若设置定时则改为异步。

    4. Promise.then()返回新的Promise结果(resolve/reject)由.then()指定的回调函数 返回值决定。如果抛出异常,则新的Promise变为rejected;如果返回非Promise任意值,变为resolved,value为返回值;如果返回值是另一个新的Promise,Promise结果就是新的Promise结果。

    5. Promise 串联多个操作任务: 1) .then()链式调用 2) 同步就直接return,异步声明新的Promise对象。

    6. Promise 异常穿透,链式调用时可以在最后指定失败的回调,通过逐层传递给最后指定的失败回调。

    7. 中断 Promise 链,返回一个pending的Promise。

      return new Promise(()=>{}) //返回一个pending的Promise
    
  • Promise、async/await、Generator

    1. 在函数名前加async,在函数体内使用await。async 是基于Promise的封装,使得链式调用更简单,代码更简洁。
    //promise
    const makeRequest = () => {
        return promise1()
            .then(value1 => {
                // do something
                return promise2(value1)
                    .then(value2 => {
                        // do something          
                        return promise3(value1, value2)
                    })
            })
    }
    // async
    const makeRequest = async () => {
      const val1 = await promise1()
      const val2 = await promise2(val1)
      return promise3(val1,val2)
    }
    
    1. Generator 函数是将函数分步骤阻塞,只有主动调用 next() 才能进行下一步。 通过function* (){} 声明。async 是Generator的语法糖。 解决以下问题:1) async函数自带执行器,2) async表示函数里有异步操作,await表示紧跟再后面的表达式需要等待结果, 3)yield命令后,只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值,4)返回值是Promise对象,可以使用then方法指定下一步操作。

  • 宏队列/微队列

    1. JS中存储执行回调函数包括2个队列:宏队列和微队列。

    2. 宏队列:用来保存执行的宏任务:定时器回调/DOM事件回调/ajax回调。

    3. 微队列:用来保存执行的微任务:Promise回调/MutationObserver回调。

    4. JS引擎首先执行所有初始化同步任务代码。每次准备取出一个宏任务执行前,都要将所有微任务一个个取出来执行。

  • 手写(自定义)Promise

    (function (window) {
        const PENDING = 'pending'
        const RESOLVED = 'resolved'
        const REJECTED = 'rejected'
        function Promise(executor) {
            // Promise对象属性
            const _this = this
            _this.status = PENDING
            _this.data = undefined
            // 存储回调函数 {onResolved, onRejected}
            _this.callbacks = []
            // 1. resolve/reject 只有当status是pendding时才能嗲用
            // 2. 只有throw /reject() 才会调用reject()
            // 3. this问题, 直接调用 resolve()/reject() this为windows,需要保存this
            // 4. 回调函数声明和状态改变无法确定先后,需要在函数内判断
            function resolve(value) {
                // 如果状态不是pendding, 不能修改
                if (_this.status !== PENDING) return
                _this.status = RESOLVED
                _this.data = value
                // 判断是否存在回调
                if (_this.callbacks.length > 0) {
                    // 异步执行回调
                    setTimeout(() => {
                        _this.callbacks.forEach(callbackObj => {
                            callbackObj.onResolved(value)
                        })
                    });
                }
            }
            function reject(reason) {
                // 如果状态不是pendding, 不能修改
                if (_this.status !== PENDING) return
                _this.status = REJECTED
                _this.data = reason
                // 判断是否存在回调
                if (_this.callbacks.length > 0) {
                    // 异步执行回调
                    setTimeout(() => {
                        _this.callbacks.forEach(callbackObj => {
                            callbackObj.onRejected(reason)
                        })
                    });
                }
            }
            try {
                executor(resolve, reject)
            } catch (error) {
                reject(error)
            }
        }
        // Promise 原型对象 .then()  指定成功/失败回调,返回新的Promise对象
        Promise.prototype.then = function (onResolved, onRejected) {
            onResolved = typeof onResolved === 'function' ? onResolved : value => value
            onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
            const _this = this
            return new Promise((resolve, reject) => {
                function handle(callback) {
                    try {
                        // 获取新的Promise返回值
                        const result = callback(_this.data)
                        // 新的Promise返回值若为Promise对象,结果是该对象的返回值
                        if (result instanceof Promise) {
                            result.then(
                                value => resolve(value),
                                reason => reject(reason)
                            )
                        } else {
                            // 新的Promise返回值不是Promise对象,结果是该执行resolve回调
                            resolve(result)
                        }
                    } catch (error) {
                        // 新的Promise返回值throw,结果是该执行reject回调
                        reject(error)
                    }
                }
                if (_this.status === PENDING) {
                    _this.callbacks.push({
                        onResolved() {
                            handle(onResolved)
                        },
                        onRejected() {
                            handle(onRejected)
                        }
                    })
                } else if (_this.status === RESOLVED) {
                    setTimeout(() => {
                        handle(onResolved)
                    });
                } else {
                    setTimeout(() => {
                        handle(onRejected)
                    });
                }
            })
        }
        // Promise 原型对象 .catch() 指定失败回调,返回新的Promise对象
        Promise.prototype.catch = function (onRejected) {
            return this.then(undefined, onRejected)
        }
        // Promise函数对象 resolve 返回结果为value的成功Promise
        Promise.resolve = function (value) {
            return new Promise((resolve, reject) => {
                // 如果value不是Promise对象,执行resolve
                if (value instanceof Promise) {
                    value.then(resolve, reject)
                } else {
                    resolve(value)
                }
            })
        }
        // Promise函数对象 reject 返回结果为value的失败Promise
        Promise.reject = function (reason) {
            // 返回失败的promise
            return new Promise((resolve, reject) => {
                reject(reason)
            })
        }
        // Promise函数对象 all 返回一个Promise,所有成功时才成功,否则返回第一个失败的
        Promise.all = function (promises) {
            const values = new Array(promises.length)
            let count = 0
            return new Promise((resolve, reject) => {
                promises.forEach((p, index) => {
                    Promise.resolve(p).then(
                        value => {
                            count++
                            values[index] = value
                            if (count === promises.length) {
                                resolve(values)
                            }
                        },
                        reason => {
                            reject(reason)
                        }
                    )
                })
            })
        }
        // Promise函数对象 race 返回第一个成功的Promise
        Promise.race = function (promises) {
            return new Promise((resolve, reject) => {
                promises.forEach((p, index) => {
                    Promise.resolve(p).then(
                        value => {resolve(value)},
                        reason => {reject(reason)}
    
                    )
                })
            })
        }
        window.Promise = Promise
    })(window)