JS之异步请求async/await(上)-基础概念和以往解决方案

183 阅读4分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

为了解决异步操作对于代码的可读性,避免回调地狱(很多个异步回调嵌套的请况),JavaScript社区先后退出Promise+thengenerator+co的解决方案,直到现在的async/await,也叫异步编程的终极解决方案,可以在不阻塞主线程的情况下用同步代码的写代码方式实现异步访问资源的能力,而且代码逻辑更清晰。 async/await的使用到的就是Generator和Promise两个技术。Promise可以看看八段代码彻底掌握 Promise,这里只写生成器概念。

生成器(Generator)和协程

生成器函数相关概念
  • function*声明的函数就是生成器函数,在生成器函数内部执行代码过程中,要是遇到yield关键字,JavaScript引擎会返回关键字后面的内容给外部,并暂停该函数的执行。
  • 可以通过函数名.next()方法恢复函数继续向下执行并会返回一个对象,有value和done两个属性,value是yeild后面的值,done的值是布尔值,代表生成器是否执行完毕,执行完毕就是true
function* genDemo() {
    console.log("开始执行第一段")
    yield 'generator 1'
​
    console.log("开始执行第二段")
    yield 'generator 2'
​
    console.log("开始执行第三段")
    yield 'generator 3'
​
    console.log("执行结束")
    return 'generator over'
}
​
console.log('main 1')
let gen = genDemo()
console.log(gen.next().value)
console.log('main 2')
console.log(gen.next().value)
console.log('main 3')
console.log(gen.next().value)
console.log('main 4')
console.log(gen.next().value)
console.log('main 5')
协程

协程是一个比线程更加轻量的存在,是跑在线程上的任务,一个线程上存在多个协程,但是在线程上同时只能执行一个协程若此时A协程正在执行,需要启动B协程,那么A协程就需要把主线程的控制权给B协程,此时A协程就暂停执行,B协程恢复执行,同理,也可以从B协程中启动A协程,如果从A协程中启动B协程,就把A协程称为B协程的父协程

一个进程可以有多个线程,一个线程也可以有多个进程,协程不是被操作系统控制的,而是程序操控,好处就是性能可以提升很多,不会像线程切换那样消耗资源。

再来分析一下上面那段代码:

  • 首先let gen = genDemo(),这个时候并没有执行,实际上是创建了gen协程
  • 然后执行gen.next(),当执行gen.next()的时候协程会继续执行,此时协程拿到主线程执行权开始执行
  • 执行到协程中的yeild关键字的时候,协程停止执行,返回yeild 'msg' 中的 msg信息,并交出主线程执行权
  • 当协程gen在执行期间碰到return的时候,JavaScript引擎会结束当前的协程,并把return后面的内容返回给父协程(创建他的协程)

结合生成器函数和Promise

fetch('https://juejin.cn/').then(function(){
    console.log('success')
})
function* foo(){
  let response1 = yield fetch('https://juejin.cn/')
  console.log('response111') 
  let response2 = yield fetch('https://www.Anblog.top') 
  console.log('response222')
}
//创建gen协程
let gen = foo()
//获取返回的promise对象
function getGenPromise(gen){
  return gen.next().value
}
//通过判断promise对象的状态,传入promise对象中的属性PromiseResult为response的内容输出出来
getGenPromise(gen).then((response)=>{
  console.log('response1')
  console.log(response)
  return getGenPromise(gen)
}).then((response)=>{
  console.log('response2')
  console.log(response)
})

步骤:

  1. 创建生成器foo函数。
  2. let gen = foo() 创建gen协程
  3. 创建getGenPromise(gen)函数,目的是执行并gen.next().value的执行结果,也就是将主线程的执行权移交gen协程的函数
  4. getGenPromise(gen)返回promise对象,然后执行执行gen协程上的fetch函数,then里传入fetch函数返回的promise对象中的response,第一个then函数执行并返回getGenPromise(gen)的结果给下一个then的response参数,然后再执行。

一般情况下会把执行生成器的代码封装成一个函数,并叫这个执行生成器的函数为执行器,上面就是生成器和Promise相互配合的一个流程。不过通常我们会把执行生成器的部分封装成一个函数。

采用co框架后代码会更精简,co函数可以代替上面的执行生成器的代码,co函数也可以叫做执行器。

const co = require("co");
const fetch=require("node-fetch")
function* foo(){
    let response1 = yield fetch('https://juejin.cn/')
    console.log('response111')
    let response2 = yield fetch('https://www.Anblog.top')
    console.log('response222')
}
co(foo());