1、async 函数的实现原理

async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。async 函数返回一个promise对象

async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {
    // ...
  });
}

所有的async函数都可以写成上面的第二种形式,其中的spawn函数就是自动执行器。

下面给出spawn函数的实现,基本就是前文自动执行器的翻版。

function spawn(genF) {
  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); // 需要return,不然无法终结递归
      }
      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); });
  });
}

2、案例

场景:假设有两个请求http://127.0.0.1:8888/getAhttp://127.0.0.1:8888/getB,第二个请求依赖第一个请求结果。

首先,封装一个ajax请求

// 使用ajax封装post请求
function post (url, params) {
    return new Promise((reslove, reject) => {
      let xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject()
      xhr.onreadystatechange = () => {
        if (xhr.readyState === 4 && xhr.status === 200) {
          // coding
          reslove(xhr.response)
        }
      }
      xhr.open('POST', url, true)
      // Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.
      // 设置请求头,请求头的设置必须在xhr打开之后,并且在send之前
      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
      xhr.setRequestHeader('Accept', 'application/json')
      xhr.send(JSON.stringify(params))
    })
  }

后端接口

const http = require('http')
// const querystring = require('querystring')

// 创建服务
http.createServer((request, response) => {
  response.writeHead(200, {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "GET, POST, DELETE, PUT, OPTIONS, HEAD",
    "Access-Control-Allow-Headers": "User-Agent, Origin, Cache-Control, Content-Type",
    "Content-Type": "application/json",
  })
  if (request.method === 'OPTIONS') {
    response.end()
  }
  if (request.url === '/getA' && request.method === 'POST') {
    let allData = ''
    request.on('data', chunk => {
      allData += chunk
    })
    request.on('end', () => {
      allData = JSON.parse(allData)
      response.end(JSON.stringify(allData))
    })
  }
  if (request.url === '/getB' && request.method === 'POST') {
    let allData = ''
    request.on('data', chunk => {
      allData += chunk
    })
    request.on('end', () => {
      allData = JSON.parse(allData)
      response.end(JSON.stringify(allData))
    })
  }
}).listen(8888)

console.log('serve is loading~~~')

使用async/await实现案例

async function handler () {
    let data1 = await post('http://127.0.0.1:8888/getA', {name: '张三'})
    return await post('http://127.0.0.1:8888/getB', Object.assign({age: 20}, JSON.parse(data1)))
}
  
handler().then(res => {console.log(res)}) // {age: 20, name: '张三'}

使用generator实现案例,需要我们手动执行

function* handler () {
    // yield 后边表达式返回的是一个promise对象
    let data1 = yield post('http://127.0.0.1:8888/getA', {name: '张三'}) // 使用next参数,不然data1 === undefined
    let data2 = yield post('http://127.0.0.1:8888/getB', Object.assign({'age': 20}, JSON.parse(data1)))
    return data2
}

var gen = handler()
// `yield`表达式本身没有返回值,或者说总是返回`undefined`。`next`方法可以带一个参数,该参数就会被当作上一个`yield`表达式的返回值。
// gen.next() 为 {value: Promise, done: false} 
// gen.next(v) 为 {value: Promise, done: false}
// gen.next(r) 为{value: {age: 20, name: '张三'}, done: true}
gen.next().value.then(v => {gen.next(v).value.then(r => {console.log(gen.next(r).value)})})

我加上一个自执行函数就可以让generator等价于async/await

function fn() {
  return spawn(handler) // 返回promise对象
}
fn().then(res => {console.log(res)}) // {age: 20, name: '张三'}

参考:
https://juejin.im/post/5bb22f...
https://juejin.im/post/5da5dc...
https://es6.ruanyifeng.com/#d...
https://juejin.im/post/5e79e8...


记得要微笑
1.9k 声望4.5k 粉丝

知不足而奋进,望远山而前行,卯足劲,不减热爱。


引用和评论

0 条评论