JavaScript异步编程

从本文你将理解到

  • 同步模式Synchronous or 异步模式Asynchronous
  • 事件循环与队列音讯
  • 异步编程的几种形式
  • Promise异步计划,宏工作/微工作列队
  • Generator异步计划,Async/Await语法糖

javascript(js)是单线程从上至下执行

同步和异步模式

同步模式

按程序排队执行,在Call stack中压入一个anonymous匿名调用,相当于把全副代码放在匿名函数中去顺次执行
阻塞
同步执行时某条操作比拟耗时会造成卡死

异步模式

不会期待工作完结再执行下一个工作,Callstack | WebAPIs | Event loop | Queue
代码是单线程的,浏览器不是,对于异步会产生一个异步调用线程

事件循环与队列音讯

  • 代码从上至下执行,顺次押入call stack主栈中
  • 遇到异步,主栈不会进行期待执行,会将异步工作压入浏览器开拓的另一个线程栈中运行
  • 两线程并行执行
  • 异步线程栈内的异步执行结束后,会将后果从异步栈中弹出至event queue,并期待主栈执行结束
  • 当主栈执行结束,通过js外部的event loop事件轮询机制将监听到的曾经在event queue队列中的异步后果安程序顺次压入主栈中执行
  • console控制台输入后果

异步编程的几种形式

异步编程计划的根本就是回调函数

回调函数

相似于一件事,你明确晓得这件事怎么做,但你不晓得这件事件所依赖的工作什么时候实现,期待工作实现后调用,回调函数领有回调天堂问题

Promise

CommonJS社区提出了Promise标准,回调函数对立解决方案
Pending -> Fufilled -> onFufilled 异步执行胜利        -> Rejected -> onRejected 异步执行失败

Promise异步计划,宏工作/微工作列队

一个根本的promise异步

const ps = new Promise(function (resolve,reject) {  resolve(100)  // reject(new Error("promise rejected"))})ps.then(function(value){  //会期待同步代码全副执行完才会执行  console.log("resolve",value)},function(err) {  console.log("reject",err)})console.log("first console")

封装一个ajax

//封装一个ajaxfunction ajax(url){  return new Promise((res,rej)=>{    let xhr = new XMLHttpRequest()    xhr.open("GET",url)    xhr.onload = function () {      if(this.readyState==4 && this.status ==200)      {        res(this.responseText)      }      else      {        rej(new Error(xhr.statusText))      }    }    xhr.send()  })}ajax("/src/api/users.json").then(        res=>console.log(res),        rej=>console.log(rej)        )

然而下面的promise并不能解决回调天堂的问题

  //回调天堂,减少复杂度  // ajax("/src/api/users.json").then((function(urls){  //   ajax("/src/api/users.json").then((function(urls){  //     ajax("/src/api/users.json").then((function(urls){      //     }))  //   }))  // }))

通过then办法链式调用

//通过promise的then办法的链式调用,使函数扁平化,防止回调嵌套//then办法返回的也是个promise//每一个then办法都是在为上一个then返回的promise对象去增加状态明确过后的回调//then办法接管的函数的参数value  是上一个then办法的返回值,如果上一个办法无返回值,则以后value=null//前面的then会期待后面的then(因为是个promise)完结后执行ajax("/src/api/users.json")  .then(value=>{    console.log(111);  }) //=>Promise  .then(value=>{      console.log(222);    return ajax("/src/api/users.json") //能够手动增加个promise返回对象  }) //=>Promise  .then(value=>{      console.log(333);  })

promise异样解决

//Promise异样解决//抛出一个异样,或者运算异样都会执行失败的回调function ajaxError(){  return new Promise((res,rej)=>{    // foo() //增加一个不存在的办法    // throw new Error("error") //手动抛异样    //...  })}

promise异样回调写法

//异样回调的写法//区别,写在then里的err函数只能捕捉到第一个promise的异样,//catch能够捕捉到链式调用中的promise异样,因为链式调用promise异样也会随上一个then返回的promise传递//catch更像是给promise链条注册回调ajaxError().then(  function () {   },  function () {  })ajaxError().then(res=>{}).then(res=>{}).catch(err=>{})

promise静态方法

  • promise.resolve()
  • promise.reject()
  • promise.all()
  • promise.race()
//Promise.resolve(参数) //参数是变量则包裹成一个promise对象//参数是promise对象则被原样返回//参数是对象,并且这个对象具备和promise一样的then办法,thenable//    利用场景是把第三方解决异步插件的then办法转化成原生的promise对象Promise.resolve("foo")   .then(value=>{    console.log(value) //"foo"  })new Promise((resolve,reject)=>{  resolve("foo")})var promise = ajax("/src/api/users.json")var promise2 = Promise.resolve(promise)console.log(promise === promise2); //truePromise.resolve({  then(onFulfilled,onRejected){    onFulfilled("foo")  }}).then(value=>{  console.log(value) //"foo"})
//Promise.reject()//疾速创立一个失败的对象
//Promise.all()并行执行//承受一个数组,数组里的每个元素是个promise对象//当数组中promise全副执行结束,则并行异步执行完结,返回一个promise对象//如果其中一个执行失败,那么这个promise就会以失败完结Promise.all([  ajax("/src/api/users.json"),  ajax("/src/api/posts.json")]).then(vals=>console.log(vals))//利用promise对象解决ajax- urls.json{    "urls":"/api/users.json",    "posts":"/api/users.json"}- ajax.tsajax("/src/api/urls.json")  .then(value=>{    const urls = Object.values(value)    const tasks = urls.map(url=>ajax(url))    return Promise.all(tasks)  })  .then(vals=>console.log(vals))
//Promise.race()//只有有一个promise对象执行实现,则进行执行返回一个new promise对象const request = ajax("/src/api/users.json") const settime = new Promise((res,rej)=>{  setTimeout(() => {    rej(new Error('timeout'))  }, 800);})Promise.race([request,settime])  .then(val=>console.log(val))   .catch(err=>console.log(err))//如果800ms内申请到users.json那么执行then办法//如果超时,则执行catch办法,(能够应用network online限速)

宏工作/微工作

//执行时序,宏工作微工作console.log("start")setTimeout(() => {  console.log("timeout")}, 0)Promise.resolve()  .then(()=>{    console.log('promise1');  })  .then(()=>{    console.log('promise2');  })  .then(()=>{    console.log('promise3');  })console.log("end");//宏工作先执行,执行完结进入回调队列的开端,//微工作后执行,执行完结在本轮回调队列的开端立刻执行//执行先宏后微,弹出先微后宏//目前绝大多数异步调用都是作为宏工作执行//Promise对象,MutationObserver对象,node中process.nextTick微工作

Generator异步计划

//promise chain//尽管解决了回调嵌套,然而仍然无奈达到同步代码的可读性ajax(1000)  .then(value=>{    ajax(2000)  })  .then(value=>{      ajax(1000)  })  .catch(err=>{      console.log(err);  })//like sync mode try{  const url = ajax("url.json")  const url1 = ajax("url1.json")  const url2 = ajax("url2.json")}catch(e){  console.error(e);   }

如何像同步代码一样执行呢

function * foo(){  console.log("start");  const res = yield 'foo' //yield的返回值是下一次next执行传入的参数  console.log(res) }const geneator = foo() //不会立刻执行,生成器对象const result = geneator.next()  //开始执行生成器中的代码console.log(result) //start  {value:"foo",done:false}const result1 = geneator.next("hello") //从上一次yield开始持续往下执行,next可传入参数,console.log(result1); //hello  {value:undefined,done:true}//done示意执行器是否执行实现,yield相似于return然而不会完结函数执行,会暂停生成器,直到再次next()调用

生成器的throw办法

function * foo1(){  try{    const res = yield 'asdasdasdsa'    // console.log(res)  }catch(e){    console.log(e)  }}const ge = foo1()//执行到第一个yield 返回对象.value就是此次yield执行后的返回值console.log(ge.next("第一次传入")) //{ value: 'asdasdasdsa', done: false }ge.throw(new Error("error1")) //抛出异样,s会被catch捕获并打印

应用生成器执行异步,like async mode

function * main(){  try{    const lastdata = yield ajaxT(1000)    // console.log(lastdata)    const lastdata1 = yield ajaxT(2000)    // console.log(lastdata)  }catch(e)  {    console.log(e)  }}const g = main()const result11 = g.next();(result11.value as Promise<any>).then(data=>{  const result22 =  g.next(data);  if(result22.done) return  (result22.value as Promise<any>).then(data=>{    const result33 = g.next(data)    if(result33.done) return  })})

封装生成器调用办法,递归

//递归封装一个生成器function co(geneator){  const g = geneator()  function handleResult(result){    if(result.done) return    result.value.then(data=>{      const nextresult = g.next(data)      handleResult(nextresult)    },err=>{      g.throw(err)    })  }  handleResult(g.next())}co(main)