• 如何写出一个惊艳面试官的 Promise


    前言

    1.高级 WEB 面试会让你手写一个Promise,Generator 的 PolyFill(一段代码); 
    2.在写之前我们简单回顾下他们的作用; 
    3.手写模块见PolyFill.

    源码

    源码地址请戳,原创码字不易,欢迎 star

    如果觉得看文章太啰嗦,可以直接 git clone ,直接看代码

    1.Promise

    1.1 作用

    Promise 大家应该都用过,ajax 库就是利用 Promise封装的; 
    作用主要是解决地狱回调问题.

    1.2 使用

    1.2.1.方法一

    new Promise((resolve,reject)=>{
      resolve('这是第一个 resolve 值')
    }).then((data)=>{
      console.log(data) //会打印'这是第一个 resolve 值'
    }).catch(()=>{
    
    })
    
    new Promise((resolve,reject)=>{
      reject('这是第一个 reject 值')
    }).then((data)=>{
      console.log(data)
    }).catch((data)=>{
      console.log(data) //会打印'这是第一个 reject 值'
    })

    1.2.2.方法二(静态方法)

    Promise.resolve('这是第二个 resolve 值').then((data)=>{
      console.log(data) // 会打印'这是第二个 resolve 值'
    })
    
    Promise.reject('这是第二个 reject 值').then((data)=>{
      console.log(data)
    }).catch(data=>{
      console.log(data) //这是第二个 reject 值
    })

    1.2.3.方法三(多个 Promise并行执行异步操作)

    表示多个 Promise 都进入到 FulFilled 或者 Rejected 就会执行

    const pOne = new Promise((resolve, reject) => {
        resolve(1);
    });
    
    const pTwo = new Promise((resolve, reject) => {
        resolve(2);
    });
    
    const pThree = new Promise((resolve, reject) => {
        resolve(3);
    });
    
    Promise.all([pOne, pTwo, pThree]).then(data => { 
        console.log(data); // [1, 2, 3] 正常执行完毕会执行这个,结果顺序和promise实例数组顺序是一致的
    }, err => {
        console.log(err); // 任意一个报错信息
    });

    1.2.4.方法四(多个中一个正常执行)

    表示多个 Promise 只有一个进入到 FulFilled 或者 Rejected 就会执行

    const pOne = new Promise((resolve, reject) => {
        resolve(1);
    });
    
    const pTwo = new Promise((resolve, reject) => {
        resolve(2);
    });
    
    const pThree = new Promise((resolve, reject) => {
        // resolve(3);
    });
    
    Promise.race([pOne, pTwo, pThree]).then(data => { 
        console.log(data); // 1 只要碰到FulFilled 或者 Rejected就会停止执行
    }, err => {
        console.log(err); // 任意一个报错信息
    });

    1.3 作用分析

    1.3.1 参数和状态

    1.Promise接受一个函数handle作为参数,handle包括resolve和reject两个是函数的参数 
    2.Promise 相当于一个状态机,有三种状态:pending,fulfilled,reject,初始状态为 pending 
    3.调用 resolve,状态由pending => fulfilled 
    4.调用reject,会由pending => rejected 
    5.改变之后不会变化

    1.3.2 then 方法

    1.接受两个参数,onFulfilled和onRejected可选的函数

    2.不是函数必须被忽略

    3.onFullfilled:
    A.当 promise 状态变为成功时必须被调用,其第一个参数为 promise 成功状态传入的值( resolve 执行时传入的值; 
    B.在 promise 状态改变前其不可被调用 
    C.其调用次数不可超过一次

    4.onRejected:作用和onFullfilled类似,只不过是promise失败调用

    5.then方法可以链式调用
    A.每次返回一个新的Promise 
    B.执行规则和错误捕获:then的返回值如果是非Promise直接作为下一个新Promise参数,如果是Promise会等Promise执行

    // 返回非Promise
    let promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve()
      }, 1000)
    })
    promise2 = promise1.then(res => {
      // 返回一个普通值
      return '这里返回一个普通值'
    })
    promise2.then(res => {
      console.log(res) //1秒后打印出:这里返回一个普通值
    })
    
    // 返回Promise
    let promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve()
      }, 1000)
    })
    promise2 = promise1.then(res => {
      // 返回一个Promise对象
      return new Promise((resolve, reject) => {
        setTimeout(() => {
         resolve('这里返回一个Promise')
        }, 2000)
      })
    })
    promise2.then(res => {
      console.log(res) //3秒后打印出:这里返回一个Promise
    })

    C. onFulfilled 或者onRejected 抛出一个异常 e ,则 promise2 必须变为失败(Rejected),并返回失败的值 e

    let promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success')
      }, 1000)
    })
    promise2 = promise1.then(res => {
      throw new Error('这里抛出一个异常e')
    })
    promise2.then(res => {
      console.log(res)
    }, err => {
      console.log(err) //1秒后打印出:这里抛出一个异常e
    })

    D.onFulfilled 不是函数且 promise1 状态为成功(Fulfilled), promise2 必须变为成功(Fulfilled)并返回 promise1 成功的值

    let promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success')
      }, 1000)
    })
    promise2 = promise1.then('这里的onFulfilled本来是一个函数,但现在不是')
    promise2.then(res => {
      console.log(res) // 1秒后打印出:success
    }, err => {
      console.log(err)
    })

    E.onRejected 不是函数且 promise1 状态为失败(Rejected),promise2必须变为失败(Rejected) 并返回 promise1 失败的值

    let promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('fail')
      }, 1000)
    })
    promise2 = promise1.then(res => res, '这里的onRejected本来是一个函数,但现在不是')
    promise2.then(res => {
      console.log(res)
    }, err => {
      console.log(err)  // 1秒后打印出:fail
    })

    F.resolve和reject结束一个Promise的调用 
    G.catch方法,捕获异常 
    其实复杂的是在then的异常处理上,不过不用急,边看边理解

    1.3.3 方法

    1.静态resolve方法
    参照1.3.1用法2

    2.静态reject方法
    参照1.3.1用法2

    3.静态all方法 
    参照1.3.1用法3

    4.静态race方法 
    参照1.3.1用法4

    5.自定义done方法 
    Promise 对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise内部的错误不会冒泡到全局) 
    因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误

     Promise.prototype.done = function (onFulfilled, onRejected) {
        this
          .then(onFulfilled, onRejected)
          .catch(function (reason) {
            // 抛出一个全局错误
            setTimeout(() => {
              throw reason
            }, 0)
          })
      }

    6.自定义finally方法 
    finally方法用于指定不管Promise对象最后状态如何,都会执行的操作 
    它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行

    Promise.prototype.finally = function (callback) {
        let P = this.constructor
        return this.then(
          value => P.resolve(callback()).then(() => value),
          reason => P.resolve(callback()).then(() => {
            throw reason
          })
        )
      }

    1.4 PolyFill

    1.4.1 初级版

    class MyPromise {
      constructor (handle) {
        // 判断handle函数与否
        if (typeof handle!=='function') {
          throw new Error('MyPromise must accept a function as a parameter')
        }
    
        // 添加状态
        this._status = 'PENDING'
        // 添加状态
        this._value = undefined
    
        // 执行handle
        try {
          handle(this._resolve.bind(this), this._reject.bind(this)) 
        } catch (err) {
          this._reject(err)
        }
      }
    
      // 添加resovle时执行的函数
      _resolve (val) {
        if (this._status !== 'PENDING') return
        this._status = 'FULFILLED'
        this._value = val
      }
    
      // 添加reject时执行的函数
      _reject (err) { 
        if (this._status !== 'PENDING') return
        this._status = 'REJECTED'
        this._value = err
      }
    }

    回顾一下,初级版实现了1,2,3这三点功能,怎么样还是so-easy吧!

    1.4.2 中级版

    1.由于 then 方法支持多次调用,我们可以维护两个数组,将每次 then 方法注册时的回调函数添加到数组中,等待执行
    在初级的基础上加入成功回调函数队列和失败回调队列和then方法

    this._fulfilledQueues = []
    this._rejectedQueues = []

    2.then方法 

    then (onFulfilled, onRejected) {
      const { _value, _status } = this
      switch (_status) {
        // 当状态为pending时,将then方法回调函数加入执行队列等待执行
        case 'PENDING':
          this._fulfilledQueues.push(onFulfilled)
          this._rejectedQueues.push(onRejected)
          break
        // 当状态已经改变时,立即执行对应的回调函数
        case 'FULFILLED':
          onFulfilled(_value)
          break
        case 'REJECTED':
          onRejected(_value)
          break
      }
      // 返回一个新的Promise对象
      return new MyPromise((onFulfilledNext, onRejectedNext) => {
      })
    }

    3.then方法规则完善

    // 添加then方法
    then (onFulfilled, onRejected) {
      const { _value, _status } = this
      // 返回一个新的Promise对象
      return new MyPromise((onFulfilledNext, onRejectedNext) => {
        // 封装一个成功时执行的函数
        let fulfilled = value => {
          try {
            if (typeof onFulfilled!=='function') {
              onFulfilledNext(value)
            } else {
              let res =  onFulfilled(value);
              if (res instanceof MyPromise) {
                // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                res.then(onFulfilledNext, onRejectedNext)
              } else {
                //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                onFulfilledNext(res)
              }
            }
          } catch (err) {
            // 如果函数执行出错,新的Promise对象的状态为失败
            onRejectedNext(err)
          }
        }
        // 封装一个失败时执行的函数
        let rejected = error => {
          try {
            if (typeof onRejected!=='function') {
              onRejectedNext(error)
            } else {
                let res = onRejected(error);
                if (res instanceof MyPromise) {
                  // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                  res.then(onFulfilledNext, onRejectedNext)
                } else {
                  //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                  onFulfilledNext(res)
                }
            }
          } catch (err) {
            // 如果函数执行出错,新的Promise对象的状态为失败
            onRejectedNext(err)
          }
        }
        switch (_status) {
          // 当状态为pending时,将then方法回调函数加入执行队列等待执行
          case 'PENDING':
            this._fulfilledQueues.push(fulfilled)
            this._rejectedQueues.push(rejected)
            break
          // 当状态已经改变时,立即执行对应的回调函数
          case 'FULFILLED':
            fulfilled(_value)
            break
          case 'REJECTED':
            rejected(_value)
            break
        }
      })
    }

    4.当 resolve 或 reject 方法执行时,我们依次提取成功或失败任务队列当中的函数开始执行,并清空队列,从而实现 then 方法的多次调用

    // 添加resovle时执行的函数
    _resolve (val) {
      if (this._status !== PENDING) return
      // 依次执行成功队列中的函数,并清空队列
      const run = () => {
        this._status = FULFILLED
        this._value = val
        let cb;
        while (cb = this._fulfilledQueues.shift()) {
          cb(val)
        }
      }
      // 为了支持同步的Promise,这里采用异步调用
      setTimeout(() => run(), 0)
    }
    // 添加reject时执行的函数
    _reject (err) { 
      if (this._status !== PENDING) return
      // 依次执行失败队列中的函数,并清空队列
      const run = () => {
        this._status = REJECTED
        this._value = err
        let cb;
        while (cb = this._rejectedQueues.shift()) {
          cb(err)
        }
      }
      // 为了支持同步的Promise,这里采用异步调用
      setTimeout(run, 0)
    }

    5.当 resolve 方法传入的参数为一个 Promise 对象时,则该 Promise 对象状态决定当前 Promise 对象的状态

    // 添加resovle时执行的函数
    _resolve (val) {
      const run = () => {
        if (this._status !== PENDING) return
        this._status = FULFILLED
        // 依次执行成功队列中的函数,并清空队列
        const runFulfilled = (value) => {
          let cb;
          while (cb = this._fulfilledQueues.shift()) {
            cb(value)
          }
        }
        // 依次执行失败队列中的函数,并清空队列
        const runRejected = (error) => {
          let cb;
          while (cb = this._rejectedQueues.shift()) {
            cb(error)
          }
        }
        /* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
          当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
        */
        if (val instanceof MyPromise) {
          val.then(value => {
            this._value = value
            runFulfilled(value)
          }, err => {
            this._value = err
            runRejected(err)
          })
        } else {
          this._value = val
          runFulfilled(val)
        }
      }
      // 为了支持同步的Promise,这里采用异步调用
      setTimeout(run, 0)
    }

    6.catch方法

    // 添加catch方法
    catch (onRejected) {
      return this.then(undefined, onRejected)
    }

    1.4.3 高级版

    1.静态 resolve 方法

    // 添加静态resolve方法
    static resolve (value) {
      // 如果参数是MyPromise实例,直接返回这个实例
      if (value instanceof MyPromise) return value
      return new MyPromise(resolve => resolve(value))
    }

    2.静态 reject 方法

    // 添加静态reject方法
    static reject (value) {
      return new MyPromise((resolve ,reject) => reject(value))
    }

    3.静态 all 方法

    // 添加静态all方法
    static all (list) {
      return new MyPromise((resolve, reject) => {
        /**
         * 返回值的集合
         */
        let values = []
        let count = 0
        for (let [i, p] of list.entries()) {
          // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
          this.resolve(p).then(res => {
            values[i] = res
            count++
            // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
            if (count === list.length) resolve(values)
          }, err => {
            // 有一个被rejected时返回的MyPromise状态就变成rejected
            reject(err)
          })
        }
      })
    }

    4.静态 race 方法

    // 添加静态race方法
    static race (list) {
      return new MyPromise((resolve, reject) => {
        for (let p of list) {
          // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
          this.resolve(p).then(res => {
            resolve(res)
          }, err => {
            reject(err)
          })
        }
      })
    }

    5.done方法 
    作用:不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局);处于回调链的尾端,保证抛出任何可能出现的错误
    目前 Promise 上还没有 done,我们可以自定义一个

    Promise.prototype.done = function (onFulfilled, onRejected) {
      console.log('done')
        this.then(onFulfilled, onRejected)
          .catch((reason)=> {
            // 抛出一个全局错误
            setTimeout(() => {
              throw reason
            }, 0)
          })
      }
    Promise.resolve('这是静态方法的第一个 resolve 值').then(()=>{
      return '这是静态方法的第二个 resolve 值'
    }).then(()=>{
      throw('这是静态方法的第一个 reject 值')
      return '这是静态方法的第二个 resolve 值'
    }).done()

    6.finally方法 
    作用:不管 Promise 对象最后状态如何,都会执行的操作 
    与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行

    finally (cb) {
      return this.then(
        value  => MyPromise.resolve(cb()).then(() => value),
        reason => MyPromise.resolve(cb()).then(() => { throw reason })
      );
    };

    7.完整代码 请戳,源码地址,欢迎 star!

    2.Generator

    2.1 定义

    1.Generator可以理解为一个状态机,内部封装了很多状态,同时返回一个迭代器Iterator对象; 
    2.迭代器Iterator对象:定义标准方式产生一个有限或无限序列值,迭代器有next()对象; 
    3.多次返回可以被 next多次调用,最大特点是可以控制执行顺序;

    2.2 声明方法

    2.是一种特殊的函数

    function* gen(x){
     const y = yield x + 6;
     return y;
    }
    
    // yield 如果用在另外一个表达式中,要放在()里面
    // 像上面如果是在=右边就不用加()
    function* genOne(x){
      const y = `这是第一个 yield 执行:${yield x + 1}`;
     return y;
    }

    整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明

    2.3 执行

    1.普通执行

    const g = gen(1);
    //执行 Generator 会返回一个Object,而不是像普通函数返回return 后面的值
    g.next() // { value: 7, done: false }
    //调用指针的 next 方法,会从函数的头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式或return语句暂停,也就是执行yield 这一行
    // 执行完成会返回一个 Object,
    // value 就是执行 yield 后面的值,done 表示函数是否执行完毕
    g.next() // { value: undefined, done: true }
    // 因为最后一行 return y 被执行完成,所以done 为 true

    2.next 方法传参数

    const g = gen(1);
    g.next() // { value: 7, done: false }
    g.next(2) // { value: 2, done: true } 
    // next 的参数是作为上个阶段异步任务的返回结果
    
    3.嵌套执行  
    必须用到yield*关键字  
    function* genTwo(x){
    yield* gen(1)
    yield* genOne(1)
    const y = 这是第 二个 yield 执行:${yield x + 2};
    return y;
    }
    const iterator=genTwo(1)
    // 因为 Generator 函数运行时生成的是一个 Iterator 对象,所以可以直接使用 for...of 循环遍历
    for(let value of iterator) {
    
    console.log(value)
    }

    2.4 yield和 return 的区别

    相同点:
    1.都能返回语句后面的那个表达式的值 
    2.都可以暂停函数执行 
    区别: 
    一个函数可以有多个 yield,但是只能有一个 return 
    yield 有位置记忆功能,return 没有

    2.5 throw

    抛出错误,可以被try...catch...捕捉到

    g.throw('这是抛出的一个错误');
    // 这是抛出的一个错误

    2.6 应用

    // 要求:函数valOne,valTwo,valThree 以此执行
    function* someTask(){
    try{
      const valOne=yield 1
      const valTwo=yield 2
      const valThree=yield 3
    }catch(e){
    
    }
    }
    
    scheduler(someTask());
    
    function scheduler(task) {
      const taskObj = task.next(task.value);
      console.log(taskObj)
      // 如果Generator函数未结束,就继续调用
      if (!taskObj.done) {
        task.value = taskObj.value
        scheduler(task);
      }
    }

    2.7 PolyFill

    原理图

    2.7.1 初级版

    实现一个迭代器(Iterator)

    // 源码实现
    function createIterator(items) {
        var i = 0
        return {
            next: function() {
                var done = (i >= items.length)
                var value = !done ? items[i++] : undefined
                
                return {
                    done: done,
                    value: value
                }
            }
        }
    }
    
    // 应用
    const iterator = createIterator([1, 2, 3])
    console.log(iterator.next())    // {value: 1, done: false}
    console.log(iterator.next())    // {value: 2, done: false}
    console.log(iterator.next())    // {value: 3, done: false}
    console.log(iterator.next())    // {value: undefined, done: true}

    2.7.2 中级版

    实现可迭代(Iterable)
    1.可以通过 for...of...遍历的对象,即原型链上有Symbol.iterator属性; 
    2.Symbol.iterator:返回一个对象的无参函数,被返回对象符合迭代器协议; 
    3.for...of接受一个可迭代对象(Iterable),或者能强制转换/包装成一个可迭代对象的值(如’abc’),遍历时,for...of会获取可迭代对象的'Symbol.iterator',对该迭代器逐次调用next(),直到迭代器返回对象的done属性为true时,遍历结束,不对该value处理;

    const a = ['a', 'b', 'c', 'd', 'e']
    
    for (let val of a) {
        console.log(val)
    }
    // 'a' 'b' 'c' 'd' 'e'
    
    // 等价于
    
    const a = ["a", "b", "c", "d", "e"]
    for (let val, ret, it = a[Symbol.iterator]();
        (ret = it.next()) && !ret.done;
        ) {
        let = ret.value
        console.log(val)
    }
    // "a" "b" "c" "d" "e"

    4.yield* 可返回一个 Iterable对象; 
    5.源码改造

    function createIterator(items) {
        let i = 0
        return {
            next: function () {
                let done = (i >= items.length)
                let value = !done ? items[i++] : undefined
                return {
                    done: done,
                    value: value
                }
            }
            [Symbol.iterator]: function () {
                return this
            }
        }
    }
    let iterator = createIterator([1, 2, 3])
    ...iterator        // 1, 2, 3
    

    2.7.3 高级版

    1.for...of...接收可迭代对象,能强制转换或包装可迭代对象的值; 
    2.遍历时,for...of会获取可迭代对象的'Symbol.iterator',对该迭代器逐次调用next(),直到迭代器返回对象的done属性为true时,遍历结束,不对该value处理; 
    3.所以可以利用 for...of...封装到原型链上.

    Object.prototype[Symbol.iterator] = function* () {
        for (const key in this) {
            if (this.hasOwnProperty(key)) {
                yield [key, this[key]]
            }
        }
    }

    3.async 和 await

    3.1 async作用

    1.async 函数返回的是一个 Promise 对象 
    在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象

    async function testAsync() {
        return "hello async";
    }
    
    const result = testAsync();
    console.log(result); //Promise 对象

    2.async和then 
    async返回一个Promise,所以可以通过then获取值

    testAsync().then(v => {
        console.log(v);    // 输出 hello async
    });

    所以async里面的函数会马上执行,这个就类似Generator的‘*’

    3.2 await作用

    1.await后面可以是Promise对象或其他表达式

    function getSomething() {
        return "something";
    }
    async function testAsync() {
        return Promise.resolve("hello async");
    }
    async function test() {
        const v1 = await getSomething();
        const v2 = await testAsync();
        console.log(v1, v2); //something 和 hello async
    }
    test();

    2.await后面不是Promise对象,直接执行

    3.await后面是Promise对象会阻塞后面的代码,Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果

    4.所以这就是await必须用在async的原因,async刚好返回一个Promise对象,可以异步执行阻塞

    3.3 async和await结合作用

    1.主要是处理Promise的链式回调或函数的地狱回调
    回到Generator中要求函数valOne,valTwo,valThree函数依次执行

    function valOne(){}
    function valTwo(){}
    function valThree(){}
    
    async ()=>{
      await valOne()
      await valTwo()
      await valThree()
    }

    2.处理异常 
    try...catch... 
    或者await .catch()

    3.4 和Generator的区别

    1.async是内置执行器,Generator 函数的执行必须依靠执行器,无需手动执行next() 
    2.更广的适用性。co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而await后面可以是任意表达式,都会返回一个Promise对象

    // Thunk函数:是能将执行结果传入回调函数,并将该回调函数返回的函数
    function f(m) {
      return m * 2;
    }
    
    f(x + 5);
    
    // 等同于
    
    var thunk = function () {
      return x + 5;
    };
    
    function f(thunk) {
      return thunk() * 2;
    }

    3.返回Promise,而Generator返回 Iterator 
    4.async 函数就是 Generator 函数的语法糖 
    async就相当于Generator的*,await相当于yield,用法有很多相似之处

    3.5 执行器PolyFill

    实现执行器两种方式: 
    回调函数(Thunk 函数) 
    Promise 对象

    3.5.1 初级版

    async function fn(args) {
      // ...
    }
    
    // 等价于
    function fn(args) {
      return spawn(function* () {
        // ...
      });
    }
    
    function spawn(gen){
      let g = gen();
    
      function next(data){
        let result = g.next(data);
        if (result.done) return result.value;
        result.value.then(function(data){
          next(data);
        });
      }
    
      next();
    }
    

    3.5.2 高级版

    function spawn(genF) { //spawn函数就是自动执行器,跟简单版的思路是一样的,多了Promise和容错处理
      return new Promise(function(resolve, reject) {
        const gen = genF();
        function step(nextF) {
          let next;
          try {
            next = nextF();
          } catch(e) {
            return reject(e);
          }
          if(next.done) {
            return resolve(next.value);
          }
          Promise.resolve(next.value).then(function(v) {
            step(function() { return gen.next(v); });
          }, function(e) {
            step(function() { return gen.throw(e); });
          });
        }
        step(function() { return gen.next(undefined); });
      });
    }

    资源搜索网站大全 https://www.renrenfan.com.cn 广州VI设计公司https://www.houdianzi.com

    4.Promise,Generator,async和await对比

    4.1 代码

    1.代码对比: 
    场景:假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。 
    A.Promise

    function chainAnimationsPromise(elem, animations) {
    
      // 变量ret用来保存上一个动画的返回值
      let ret = null;
    
      // 新建一个空的Promise
      let p = Promise.resolve();
    
      // 使用then方法,添加所有动画
      for(let anim of animations) {
        p = p.then(function(val) {
          ret = val;
          return anim(elem);
        });
      }
    
      // 返回一个部署了错误捕捉机制的Promise
      return p.catch(function(e) {
        /* 忽略错误,继续执行 */
      }).then(function() {
        return ret;
      });
    
    }

    B.Generator

    function chainAnimationsGenerator(elem, animations) {
    
      return spawn(function*() {
        let ret = null;
        try {
          for(let anim of animations) {
            ret = yield anim(elem);
          }
        } catch(e) {
          /* 忽略错误,继续执行 */
        }
        return ret;
      });
    
    }

    C.async

    async function chainAnimationsAsync(elem, animations) {
      let ret = null;
      try {
        for(let anim of animations) {
          ret = await anim(elem);
        }
      } catch(e) {
        /* 忽略错误,继续执行 */
      }
      return ret;
    }

    对比可以看出 async...await...代码更优雅

    4.2 原理

    async 和 await 是在 Generator 的基础上封装了自执行函数和一些特性; 
    具体对比见没个 API 的 PolyFill

    4.3 执行顺序

    来道头条的面试

    console.log('script start')
    
    async function async1() {
    await async2()
    console.log('async1 end')
    }
    async function async2() {
    console.log('async2 end')
    }
    async1()
    
    setTimeout(function() {
    console.log('setTimeout')
    }, 0)
    
    new Promise(resolve => {
    console.log('Promise')
    resolve()
    })
    .then(function() {
    console.log('promise1')
    })
    .then(function() {
    console.log('promise2')
    })
    
    console.log('script end')
    
    // 旧版 Chrome 打印
    // script start => async2 end => Promise => script end => promise1 => promise2 => async1 end => setTimeout
    
    // 新版 Chrome 打印
    // script start => async2 end => Promise =>  script end => async1 end => promise1 => promise2 => setTimeout
    
    // 这里面其他的顺序除了async1 end , promise1 , promise2 这几个顺序有点争议,其他应该没有什么问题
    
    // 新版是因为V8 团队将最新的规范进行了修改,await变得更快了,这道题细节分析不再赘述,上面原理都有讲到
  • 相关阅读:
    MyEclipse 2015 Stable 2.0破解方法
    GeoGlobe Server运维
    GeoGlobe Server运维
    Silverlight用户无法注册之MySql.Data.dll不一致
    Photoshop影像匀色技术
    GeoGlobe Server使用问题收集
    Windows Server 2008 R2中无法使用360免费Wifi的解决方案
    吉奥平台软件安装经验分享
    U盘中毒后变为快捷方式的解决方法
    主机访问虚拟机中新建的网站
  • 原文地址:https://www.cnblogs.com/xiaonian8/p/14096695.html
Copyright © 2020-2023  润新知