基本概念传统的JavaScript异步编程形式大致分为以下几种。回调函数事件监听发布/订阅Promise对象异步任务的持续执行称为同步。如果任务分两步执行,第一步执行完后,再执行其他任务,准备就绪后再执行第二步。这种不连续的执行称为异步。回调函数回调函数就是将第二步执行的任务单独写在一个函数中,当任务再次执行时直接调用这个函数。回调函数的英文叫做callback,直译就是“召回”。loadData(url,function(data){console.log(data);});注意:task的第一个step执行完后,context就结束了,所以我们一般用varthat=this来设置第一个step执行的时候保存this点,这样在callback中就可以使用了。函数Api(url){this.url=url;this.request=function(){varthat=thissetTimeout(function(){console.log('url',that.url)},1000)}}varapi=newApi('http://127.0.0.1')api.request()//urlhttp://127.0.0.1Generator函数在异步编程方案中,ES6也提供了Generator函数。它其实就是一个普通的函数,函数关键字和函数名之间有一个星号作为特有的功能;yield表达式在函数体内使用来定义不同的内部状态。function*statusGenerator(){yield'pending';产生“运行”;返回'结束';}varst=statusGenerator();上面代码statusGenerator函数返回一个迭代器对象,函数中定义了三种状态,调用迭代器的next方法指向下一个状态。st.next()//{value:'pending',done:false}st.next()//{value:'running',done:false}st.next()//{value:'end',done:false}yield表达式yield表达式是暂停标志。当迭代器接下来执行时。当遇到yield表达式时,暂停后续操作,将yield后面的表达式的值作为返回对象的value属性值。下次调用next方法时,继续执行直到遇到下一个yield表达式。如果没有遇到新的yield表达式,会一直运行到函数结束,直到return语句,return语句之后的表达式的值会作为返回对象的value属性值。如果函数没有返回语句,则返回对象的值属性未定义。for...of循环我们也可以使用for...of来遍历。function*statusGenerator(){yield'pending';产生“运行”;return'end';}varst=statusGenerator();for(letvofst){console.log(v)//pendingrunning}Generator的Application协程协程是指多个线程相互配合完成异步任务。它是一些编程语言的异步编程解决方案,比如go中的协程实现goroutine。协程执行的大致流程如下:协程A开始执行。协程A执行到一半,进入暂停,执行权转移给协程B。(一段时间后)协程B归还执行权。协程A恢复执行。JavaScript中的协程实现了Generator函数,可以在指定的地方(yield)交出函数的执行权(即暂停执行),然后等待执行权归还继续执行。比如:我们实现一个倒计时功能,等待任务准备好后倒计时,一起执行。function*countdown(num,running){do{yieldnum--}while(num>0)running()}consttasks=[]constct=countdown(3,function(){console.log('开始运行任务')for(lettaskoftasks){task()}})for(leti=0;i<3;i++){tasks.push(function(){console.log('task'+i)})ct.next()}ct.next()一次异步请求封装varfetch=require('node-fetch');function*request(){varurl='xxxx';varuser=yieldfetch(url);//返回promise对象,data:{'user':'xxxx'}console.log(user);}varreq=request();varresult=req.next();result.value.then(function(data){returndata.user}).then(function(user){req.next(user);//将用户信息传递给request()函数,由user变量接收。});asyncfunctionES2017引入了async和await关键字,使用这对关键字,你可以更简洁地编写基于Promise的异步行为,而无需刻意将promises串起来。异步声明的函数一般称为异步函数。你可以将async视为Generator的语法糖,因为它们具有相同的基本功能。生成器写法constloadData=function(url){returnnewPromise(function(resolve,reject){resolve(data);});};constrequest=function*(){constuser=yieldloadData('https://user');constgoods=yieldloadData('https://goods');console.log(用户,商品);};异步写入constloadData=function(url){returnnewPromise(function(resolve,reject){resolve(data);});};constrequest=asyncfunction(){constuser=awaitloadData('https://user');constgoods=awaitloadData('https://goods');控制台.log(用户,商品);};基本用法异步函数将返回一个Promise对象。函数在执行时,一旦遇到await,会先返回,等待异步操作完成,然后在函数体中执行下面的语句。functiontimeout(ms){returnnewPromise((resolve)=>{setTimeout(resolve,ms);});}asyncfunctionasyncPrint(value,ms){awaittimeout(ms);console.log(value);}asyncPrint('你好',50);async函数内部return语句返回的值会成为then方法回调函数的参数。asyncfunctionhello(){return'hello';}hello().then(v=>console.log(v))//"hello"在async函数内部抛出一个错误,会导致返回的Promise对象成为拒绝。抛出的错误对象会被catch方法回调函数接收。asyncfunctionhello(){thrownewError('Error');}hello().then(v=>console.log(v),e=>console.log(e))////Error:Erorawait命令一般来说,await后面跟着一个Promise对象,返回对象的结果。如果不是Promise对象,直接返回对应的值。asyncfunctionhello(){returnawait'hello'}hello().then(v=>console.log(v))//helloasyncfunctionhello(){returnawaitPromise.resolve('hello');}hello().then(v=>console.log(v))//hello错误处理如果await之后的异步操作失败,则拒绝async函数返回的Promise对象。asyncfunctionhello(){awaitnewPromise(function(resolve,reject){thrownewError('error');});}hello().then(v=>console.log(v)).catch(e=>console.log(e))//Error:error所以最好把await命令放在try...catch代码块中。asyncfunctionhello(){try{awaitnewPromise(function(resolve,reject){thrownewError('error');});}catch(e){控制台。log('err:',e)//错误}returnawait('hello');}consth=hello();h.then((v)=>{console.log(v)})//你好总结本文记录了JavaScript异步编程中的一些方法,Generator函数以及async和await语法,欢迎留言交流。
