前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何写出一个惊艳面试官的 Promise【近 1W字】 前言源码1.Promise2.Generator3.async 和 await4.Pro

如何写出一个惊艳面试官的 Promise【近 1W字】 前言源码1.Promise2.Generator3.async 和 await4.Pro

作者头像
火狼1
发布2020-05-09 16:05:31
6250
发布2020-05-09 16:05:31
举报

前言

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

源码

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

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

1.Promise

1.1 作用

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

1.2 使用

1.2.1.方法一

代码语言:javascript
复制
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.方法二(静态方法)

代码语言:javascript
复制
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 就会执行

代码语言:javascript
复制
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 就会执行

代码语言:javascript
复制
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执行

代码语言:javascript
复制
// 返回非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

代码语言:javascript
复制
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 成功的值

代码语言:javascript
复制
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 失败的值

代码语言:javascript
复制
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方法,总是处于回调链的尾端,保证抛出任何可能出现的错误

代码语言:javascript
复制
 Promise.prototype.done = function (onFulfilled, onRejected) {
    this
      .then(onFulfilled, onRejected)
      .catch(function (reason) {
        // 抛出一个全局错误
        setTimeout(() => {
          throw reason
        }, 0)
      })
  }

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

代码语言:javascript
复制
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 初级版

代码语言:javascript
复制
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方法

代码语言:javascript
复制
this._fulfilledQueues = []
this._rejectedQueues = []

2.then方法 将

代码语言:javascript
复制
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方法规则完善

代码语言:javascript
复制
// 添加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 方法的多次调用

代码语言:javascript
复制
// 添加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 对象的状态

代码语言:javascript
复制
// 添加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方法

代码语言:javascript
复制
// 添加catch方法
catch (onRejected) {
  return this.then(undefined, onRejected)
}

1.4.3 高级版

1.静态 resolve 方法

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

2.静态 reject 方法

代码语言:javascript
复制
// 添加静态reject方法
static reject (value) {
  return new MyPromise((resolve ,reject) => reject(value))
}

3.静态 all 方法

代码语言:javascript
复制
// 添加静态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 方法

代码语言:javascript
复制
// 添加静态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,我们可以自定义一个

代码语言:javascript
复制
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方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行

代码语言:javascript
复制
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.是一种特殊的函数

代码语言:javascript
复制
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.普通执行

代码语言:javascript
复制
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 方法传参数

代码语言:javascript
复制
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) {

代码语言:javascript
复制
console.log(value)

}

2.4 yield和 return 的区别

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

2.5 throw

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

代码语言:javascript
复制
g.throw('这是抛出的一个错误');
// 这是抛出的一个错误

2.6 应用

代码语言:javascript
复制
// 要求:函数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)

代码语言:javascript
复制
// 源码实现
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处理;

代码语言:javascript
复制
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.源码改造

代码语言:javascript
复制
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...封装到原型链上.

代码语言:javascript
复制
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 对象

代码语言:javascript
复制
async function testAsync() {
    return "hello async";
}

const result = testAsync();
console.log(result); //Promise 对象

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

代码语言:javascript
复制
testAsync().then(v => {
    console.log(v);    // 输出 hello async
});

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

3.2 await作用

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

代码语言:javascript
复制
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函数依次执行

代码语言:javascript
复制
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对象

代码语言:javascript
复制
// 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 初级版

https://juejin.im/post/5dc28e...

代码语言:javascript
复制
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 高级版

代码语言:javascript
复制
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); });
  });
}

4.Promise,Generator,async和await对比

4.1 代码

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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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 执行顺序

来道头条的面试

代码语言:javascript
复制
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变得更快了,这道题细节分析不再赘述,上面原理都有讲到

后语

原创码字不易,欢迎 star!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 源码
  • 1.Promise
    • 1.1 作用
      • 1.2 使用
        • 1.2.1.方法一
        • 1.2.2.方法二(静态方法)
        • 1.2.3.方法三(多个 Promise并行执行异步操作)
        • 1.2.4.方法四(多个中一个正常执行)
      • 1.3 作用分析
        • 1.3.1 参数和状态
        • 1.3.2 then 方法
        • 1.3.3 方法
      • 1.4 PolyFill
        • 1.4.1 初级版
        • 1.4.2 中级版
        • 1.4.3 高级版
    • 2.Generator
      • 2.1 定义
        • 2.2 声明方法
          • 2.3 执行
            • 2.4 yield和 return 的区别
              • 2.5 throw
                • 2.6 应用
                  • 2.7 PolyFill
                    • 2.7.1 初级版
                    • 2.7.2 中级版
                    • 2.7.3 高级版
                • 3.async 和 await
                  • 3.1 async作用
                    • 3.2 await作用
                      • 3.3 async和await结合作用
                        • 3.4 和Generator的区别
                          • 3.5 执行器PolyFill
                            • 3.5.1 初级版
                            • 3.5.2 高级版
                        • 4.Promise,Generator,async和await对比
                          • 4.1 代码
                            • 4.2 原理
                              • 4.3 执行顺序
                              • 后语
                              相关产品与服务
                              容器服务
                              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档