?这里是CallbackHell早些年,你会看到很多解决方案,比如Q,async,EventProxy等。最后,从流行度来看,Promise是当之无愧的,它是ES6中的Javascript标准基础知识/概念请参考阮一峰的prommisobject。实现案例可以参考我的另一篇文章Promise初探面试事件/异步相关问题函数式编程基础看这里函数式编程入门教程高阶函数函数参数一般只接受原始数据类型或对象引用。以下是正常的参数传递和返回:functionfoo(x){returnx;}高阶函数可以将函数作为参数或返回函数作为值:functionfoo(x){returnfunction(){returnx;};}跟进式编程跟进式编程将函数的业务重心从返回值转移到了回调函数上:functionfoo(x,bar){returnbar(x);}相对于同一个foo函数,可以传入不同的bar参数,得到不同的结果。一个典型的例子是数组sort()方法varpoints=[40,100,1,5,25,10];points.sort(function(a,b){returna-b;});通过改变sort()方法的参数可以决定不同的排序方式,从中可以看出高阶函数的灵活性。Node中处理事件的方式是基于高阶函数的特性。通过为同一个事件注册不同的回调函数,可以灵活处理业务逻辑。varemitter=newevents.EventEmitter();emitter.on('event_foo',function(){//做某事});JavaScript中有很多高阶函数,其中ECMAScript5提供的一些数组方法非常典型。forEach()reduce()reduceRight()filter()every()some()偏函数首先定义:通过指定一些参数生成新的自定义函数的形式是偏函数vartoString=Object.prototype.toString;varisString=function(obj){返回字符串。call(obj)=='[objectString]';};varisFunction=function(obj){返回字符串。call(obj)=='[对象函数]';};在javascript中进行类型判断时,我们通常会像上面的代码那样定义方法。这两个函数的定义重复定义了相似的函数。如果is*()多了,就会产生很多冗余代码。我们可以使用isType()函数预先指定类型的值varisType=function(type){returnfunction(obj){returntoString.call(obj)=='[object'+type']';};};varisString=isType('字符串');varisFunction=isTyp('函数');可以看出,引入isType()函数后,创建isString()和isFunction()函数变得简单多了。偏函数应用在异步编程中也很常见。异步编程的优点和难点异常处理的优点和难点我们以前用这样的代码来捕获异常:注意输出顺序,同步回调异步异步回调setTimeout(()=>{console.log(1,newDate());setTimeout(()=>{console.log(2,newDate());setTimeout(()=>{console.log(3,newDate());},2000)},1000);},1000);AJAXwindow.onload=function(){varhttp=newXMLHttpRequest();http.onreadystatechange=function(){if(http.readyState==4&&http.status==200){console.log(JSON.parse(http.response));}};http.open("GET","data/jx.json",true);//true异步false同步http.send();console.log('测试');当我们启动服务器时,第一个日志是测试然后是服务器返回的响应对象是一个典型的异步console.log(JSON.parse(http.response));如果改为这个http.open("GET","data/jx.json",false);//trueasynchronousfalsesynchronous会先输出对象再输出测试,但是如果这样是错误的,不要这样用。请写下这个:$.get("data/jx.json",function(data){console.log(data);});console.log('测试');};澄清一些误解:Promises不能替代回调Promises通过在回调代码和将执行任务的异步代码之间提供可靠的中介来管理回调。Promise可以链接在一起,将一系列异步完成的步骤串在一起。结合更高级别的抽象,如all(..)方法(在经典术语中称为“门”)和race(..)方法(在经典术语中称为“闩锁”),Promise链提供了一个近似的异步控制流动。定义Promise的另一种方法是将其视为未来值,一个与时间无关的值包装器。无论这个容器的底层价值是否已经最终确定,它的价值都可以以同样的方式应用。一旦观察到Promise的解析,就提取值。换句话说,Promises可以被认为是同步函数返回值的异步版本。--摘自《你不知道的JavaScript 下卷》其实es6已经支持原生promise:Promiseobjectjquerypromise(Promise/Deferredmode)这个实现解决了我们在文章开头提到的回调地狱问题。我们可以从下面的例子看出:setTimeout(()=>{console.log(4);},0);newPromise((resovle=>{console.log(1);resovle();console.log(2);})).then(()=>{console.log(5);});console.log(3);//问题:为什么输出的是12354而不是12345?Promise构造函数接受一个函数作为参数,函数的两个参数分别是resolve和reject。Promise实例生成后,可以使用then方法分别指定Resolved状态和Reject状态的回调函数。上面代码中,Promise创建后立即执行,所以最先输出的是“1”、“2”。然后then方法指定的回调函数直到当前脚本的所有同步任务都执行完才会执行,所以then输出“3”(1)所有同步任务都在主线程执行,形成一个执行上下文栈。(2)除了主线程外,还有一个“任务队列”,只要异步任务有运行结果,就在“任务队列”中放入一个事件。(3)一旦全部同步“执行栈”中的任务执行完毕后,系统会读取“任务队列”,看看里面有哪些事件,那些对应的异步任务结束等待状态,进入执行栈,开始执行。(4)mainthread不断重复上面的第三步,在Node.js事件循环中:Macrotask和Microtask异步任务两类,在挂起任务的时候,JS引擎会将所有的任务按照类别划分到这两个队列中,首先取出第一个任务从宏任务队列(这个队列也叫任务队列)中取出,执行完后从微任务队列中取出,队列中的所有任务依次执行;之后,取出macrotask任务,如此循环,直到两个队列中的任务全部取出。1.宏任务:脚本(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI渲染2.微任务:process.nextTick、Promises(这里指的是浏览器实现的原生Promise)、Object.observe、MutationObserver来源:图灵社区:Promise/A+规范可以这么简单的理解:如果你想要一个task立即执行,则设置为Microtask,否则最好使用Macrotask。因为可以看出Node虽然是异步非阻塞的,但是在一个事件循环中,Microtask的执行方式基本是同步的。这是console.log(3);执行后立即执行Promise返回函数,然后setTimeout()返回函数构造和promise的使用。首先我们要知道Promise的解析结果只有两种可能:completion或者Deny,可选单值。如果Promise被fulfilled,最终的值称为fulfillmentvalue;如果被拒绝,则最终值称为原因(即“拒绝原因”)。Promise只能被解决(完成或拒绝)一次。随后的完成或拒绝尝试将被忽略。因此,一旦Promise被resolved,它就是一个不变的,不能被改变。可以通过构造函数Promise(..)构造一个promise实例:varp=newPromise(function(resolve,reject){//..});提供给构造函数Promise(..)的两个参数都是函数,通常称为resolve(..)和reject(..)。这就是它们的使用方式。如果reject(..)被调用,promise将被拒绝,如果任何值被传递给reject(..),该值将被设置为拒绝的原因。如果调用resolve(..)时没有传入任何值,或者传入任何非承诺值,则承诺得到履行。如果您调用resolve(..)并传入另一个承诺,则该承诺将采用传入承诺的状态(已实现或已拒绝)——无论是立即的还是最终的。这是通过承诺重构回调函数调用的常用方法。假设您最初使用的是需要能够调用errorfirst样式回调的ajax(..)工具:functionajax(url,cb){//构建请求,最终将调用cb(..)}//。.ajax("http://some.url.1",functionhandler(err,contents){if(err){//处理ajax错误}else{//处理内容成功}});可以转化为:functionajax(url){returnnewPromise(functionpr(resolve,reject){//创建请求,最终调用resolve(..)orreject(..)});}//..ajax("http://some.url.1").then(functionfulfilled(contents){//处理内容成功},functionrejected(reason){//处理ajax错误原因});setTimeout(()=>{console.log(4);},0);newPromise((resolve)=>{console.log(1);resolve();console.log(2);}).then(()=>{console.log(5);newPromise((resolve)=>{console.log(6);resolve();console.log(7);}).then(()=>{console.log(8);setTimeout(()=>{console.log(9);},0)})});console.log(3);//Output:123567849Promise在Node中的实现我们通过继承Node的events模块来完成简单的实现。看这里《深入浅出》4.3有时间整理总结生成器(es6)Generator+Promise可以将一系列的promise表达成一个链条来表示程序的异步流控。通常,我们编写链式promisestep1().then(step2,step2Failed).then(function(msg){returnPromise.all([step3a(msg),step3b(msg),step3c(msg)])}).then(步骤4);然而,有一种更好的方式来表达异步流控制,而且从编码规范来看,它也比长承诺链更容易实现。我们可以使用生成器来表达我们的异步流控制。生成器可以产生一个承诺,然后可以绑定该承诺以使用其实现值恢复运行生成器。上面的代码可以这样改造:从表面上看,这段代码似乎比前面代码中等效的promise链实现更冗长。然而,它提供了一种更具吸引力,同时更重要的是,更易于理解、更合理且看似同步的编码风格(通过=赋值给“返回”值等)。特别是因为try..catch错误处理可以跨越这些隐藏的异步边界。Generator有多种语言版本,它本质上是一个协程。我们先来看看协程、线程、进程的区别和联系:进程:操作系统中资源分配的基本单位。线程:操作系统中资源调度的基本单位。进程:比线程更小的执行单元,有自己的cpu上下文。一个协程,一个堆栈,一个进程可能有多个线程,一个线程可能有多个协程。进程和线程之间的切换由操作系统控制。协程的切换由程序员自己控制。异步i/o使用回调来处理密集型i/o。使用协同程序也可以做到这一点。协程的切换不会浪费很多资源。一个I/O操作写成一个协程,这样在I/O的时候,可以把CPU让给其他协程。js也支持协程,也就是yield。使用yield给我们一个直观的感觉,执行到这里就停止了,其他代码继续运行。当你想让它继续时,它就会继续执行。这是一个用q库实现的Generator函数使用教程。现在es6内置了Generator功能。es6生成器函数语法迭代器在执行next()时总是返回一个对象。对象中有两个属性,状态的返回值done生成器函数内部迭代是否完成,所以控制着代码的执行进度,所以很容易用生成器函数来解决异步,没有回调,甚至没有promise,通过同步的方式(鲸注:现在node8之后已经全面支持async/await而不是yield)并且koa框架在v3版本koa@2.4application.js中将不再支持yield源代码回调->promise->yieldfunctionp(time){returnnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(newDate());},time)});}p(1000).then((data)=>{console.log(1,data);返回p(1000);}).then((data)=>{console.log(2,data);返回p(2000);}).then((data)=>{console.log(3,data);})functionp(time){returnnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(newDate());},time)});}co(function*delay(){让time1=yieldp(1000);console.log(1,time1);让time2=yieldp(1000);console.log(2,time2)让time3=yieldp(2000);console.log(3,time3);})函数co(gen){让它=gen();下一个();函数next(arg){让ret=it.next(arg);如果(ret.done)返回;ret.value.then((data)=>{next(data)})}}Example-asyncandawaitimplementation(es7)体验异步的终极解决方案-ES7的Async/Awaitfunctionp(time){returnnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(newDate());},time)});}(asyncfunction(){lettime1=awaitp(1000);console.log(1,time1);lettime2=awaitp(1000);console.log(2,time2)lettime3=awaitp(2000);console.log(3,time3);})()function*foo(x){让y=x*(产量);returny;}letit=foo(6);letres=it.next();//什么是资源?...React中fetch实战教程(六)使用fetchfetch是一种可以替代ajax获取/提交数据的技术。一些高级浏览器已经可以使用window.fetch。与jQuery.ajax相比,轻量级(只做这一件事),并且原生支持promises,更符合现在的编程习惯。参考《你不知道的JavaScript》讲讲callback、asynchronous和generatorGenerator函数的含义和用法。Javascript异步编程的4种方法。.EventProxyAsync/等待
