async await原理

185 阅读4分钟

说到async得先说下Generator函数

Generator

Generator 函数是 ES6 提供的一种异步编程解决方案

function* helloWorld() {
  yield 'hello'
  yield 'world'
  return 'ending'
}
let hw = helloWorld()

执行结果

console.log(hw.next()) //打印 {value: "hello", done: false}
console.log(hw.next()) //打印 {value: "world", done: false}
console.log(hw.next()) //打印 {value: "ending", done: true}
console.log(hw.next()) //打印 {value: undefined, done: true}

Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

yield

Generator 函数如果是使用yield表达式,就变成了一个单纯的__暂缓执行函数__。

function* helloWorld() {
    console.log('hello' + ' world')
}
let hw = helloWorld()

setTimeout(() => {
    hw.next()
}, 1000)
//1秒后打印 hello world
next

yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值

function* f(x) {
  var y = 2 * (yield (x + 1))
  var z = yield (y / 3)
  return (x + y + z)
}

let g = f(0);
//输出0(注意第一次使用next方法时,传递参数是无效的)
console.log(g.next())
//x等于0, y等于120(2*60)输出40 (120/3)
console.log(g.next(60))
//x等于0,y等于120, z等于200 输出320 (0 + 120 + 200)
console.log(g.next(200))

Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next方法的参数,就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值。也就是说,可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。

异步执行

function* f() {
    const data = yield (new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('hello async')
        }, 1000)
    }))
    console.log('hello world')
}


let g = f()

const data = g.next()
data.value.then(data => {
    return data
}).then(data => {
    //data == hello async
    g.next() //打印hello world
})

Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。

Thunk 函数

Thunk 函数可以用于 Generator 函数的自动流程管理,下面的run方法就是一个Generator 函数的自动执行器

function* f() {
    const f1 = yield (new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('hello async A')
        }, 1000)
    }))
    const f2 = yield (new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('hello async B')
        }, 1000)
    }))
    console.log(f1, f2)
}


function run(fn) {
    let gen = fn()
    function next(data) {
      let result = gen.next(data)
      if (result.done) return
      result.value.then(data => {
          next(data)
      })
    }
    next()
  }
  
run(f)
//输出 hello async A hello async B

co模块

co模块用于 Generator 函数的自动执行

const co = require('co')

function* f() {
    const f1 = yield (new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('hello async A')
        }, 1000)
    }))
    const f2 = yield (new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('hello async B')
        }, 1000)
    }))
    console.log(f1, f2)
}


co(f).then(data => {
	console.log('执行完成啦')
})

co 模块可以让你不用编写 Generator 函数的执行器,Generator 函数只要传入co函数,就会自动执行,co函数返回一个promise对象

co模块部分源码,其接收一个Generator函数作为参数

function co(gen) {
    var ctx = this;
    var args = slice.call(arguments, 1);
    return new Promise(function(resolve, reject) {
      if (typeof gen === 'function') gen = gen.apply(ctx, args);
      if (!gen || typeof gen.next !== 'function') return resolve(gen);
  
      onFulfilled();
  
      function onFulfilled(res) {
        var ret;
        try {
          ret = gen.next(res);
        } catch (e) {
          return reject(e);
        }
        next(ret);
        return null;
      }
  
      function onRejected(err) {
        var ret;
        try {
          ret = gen.throw(err);
        } catch (e) {
          return reject(e);
        }
        next(ret);
      }
  
      function next(ret) {
        //ret.done 为true 表示遍历介绍 ret.value 为 Generator函数return返回的表达式,没有的话就是undefined
        if (ret.done) return resolve(ret.value);
        var value = toPromise.call(ctx, ret.value);
        //调用 onFulfilled函数 执行下一个 next
        if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
        //yield 后面 必须是 function, promise, generator, array, object数据类型
        return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
          + 'but the following object was passed: "' + String(ret.value) + '"'));
      }
    })
}

async await

function* f() {
    const f1 = yield (new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('hello async A')
        }, 1000)
    }))
    const f2 = yield (new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('hello async B')
        }, 1000)
    }))
    console.log(f1, f2)
}

async function g(){
    const f1 = await (new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('hello async A')
        }, 1000)
    }))
    const f2 = await (new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('hello async B')
        }, 1000)
    }))
    console.log(f1, f2)
}

async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已

优点

1、内置执行器 Generator函数必须依赖执行器执行,比如co模块,async函数自带执行器,执行方式和普通函数一样

g()//打印 hello async A hello async B

2、更好的语义 async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果

3、更广的适用性 4、返回值是promise