JavaScript语言的执行环境是单线程的,异步编程对于JavaScript来说必不可少。JavaScript传统的异步解决方案主要是通过回调函数,而回调函数最大的问题就是CallbackHell。所以ES6标准提供的Promise对象就是专门用来解决异步编程问题的。Java语言是一种支持多线程的语言,它的语法主要是同步的。在实际开发中,很少需要大量的异步编程。但如果你想追求更高的性能,异步通常是更好的选择。比如Servlet3的异步支持,Spring5提供的SpringWebFlux,都是为了追求更高的性能。和JavaScript一样,传统的处理Java异步的Callback方式也会存在CallbackHell问题,所以在Java8中加入了一个类似于ES6的Promise的对象:java.util.concurrent.CompletableFuture。创建一个对象创建一个Promise在JavaScript中创建一个Promise对象:constpromise=newPromise(function(resolve,reject){if(success){//successresolve(value);}else{//failurereject(error);}});例如,jQuery使用回调函数编写ajax的传统方式是这样的:$.ajax({url:"/url",success:function(data){//success},error:function(jqXHR,textStatus,error){//失败}});如果你想封装它并返回一个Promise对象,你可以这样写:(data){//将Promise更新为成功状态resolve(data);},error:function(jqXHR,textStatus,error){//将Promise更新为失败状态reject(error);}});});returnpromise;}调用这个封装的promisifyAjax方法:promisifyAjax("/url").then(function(data){//success}).catch(function(error){//failure});创建完成etableFuture在Java中创建一个CompletableFuture对象:CompletableFuturecompletableFuture=newCompletableFuture<>();if(success){completableFuture.complete("tasksuccessful");}else{completableFuture.completeExceptionally(newRuntimeException("taskfailed")));}CompletableFuture可以使用泛型指定任务成功后返回结果的数据类型这里可以通过OkHttp异步请求来实际使用CompletableFuture。OkHttp异步请求官方例子中使用的异步实现方法是基于Callback回调的方法:privatefinalOkHttpClientclient=newOkHttpClient();publicvoidrun()throwsException{Requestrequest=newRequest.Builder().url("http://publicobject.com/helloworld.txt").build();client.newCall(request).enqueue(newCallback(){@OverridepublicvoidonFailure(Callcall,IOExceptione){//HTTP请求异常e.printStackTrace();}@OverridepublicvoidonResponse(Callcall,Responseresponse)throwsIOException{try(ResponseBodyresponseBody=response.body()){if(!response.isSuccessful()){//响应状态码异常thrownewIOException("Unexpectedcode"+response);}else{//successSystem.out.println(responseBody.string());}}}});}下面将这个异步请求封装为返回CompletableFuture的方法:publicstaticCompletableFutureasyncRequest(Stringurl){CompletableFuturecompletableFuture=newCompletableFuture<>();请求request=newRequest.Builder().url(url).build();client.newCall(request).enqueue(newCallback(){@OverridepublicvoidonFailure(Callcall,IOExceptione){//HTTP请求异常completableFuture.completeExceptionally(e);}@OverridepublicvoidonResponse(Callcall,Responseresponse)throwsIOException{try(ResponseBodyresponseBody=response.body()){if(!response.isSuccessful()){//响应状态代码异常completableFuture.completeExceptionally(newIOException("Unexpectedcode"+response));}else{//成功completableFuture.complete(responseBody.string());}}}});}使用封装的asyncRequest()方法:asyncRequest("/url").thenAccept(responseText->{//success}).exceptionally(e->{//failure});可以看到这种写法和ES6Promise的写法差不多。基本上,使用上面的jQuery.ajax()函数来封装并返回Promise对象就是学习Promise对象是如何创建的。其实已经有一个非常不错的开源项目Axios,它是一个基于Promise的HTTP客户端,同时支持浏览器Ajax和Node.js:axios.get('/user?ID=12345').then(function(response){//处理成功console.log(response);}).catch(function(error){//处理错误console.log(error);});此外,Java11中新增的java.net.http.HttpClient自然支持CompletableFuture:publicstaticvoidmain(String[]args)throwsInterruptedException{HttpRequest请求=HttpRequest.newBuilder().uri(URI.create("http://xxx.com/")).build();CompletableFuture>responseFuture=client.sendAsync(request,HttpResponse.BodyHandlers.ofString());responseFuture.thenAccept(response->{System.out.println(response.body());}).exceptionally(e->{System.out.println(e);returnnull;});//防止程序在主线程结束后停止线程.sleep(Integer.MAX_VALUE);}then链式调用ES6Promise的then方法的返回值也是一个Promise,所以可以链式调用:axios.get('/request1').then(function(response){//把第一个请求的结果作为第二个请求的参数,返回一个新的Promise作为下一个的结果thenconstnewPromise=axios.get('/request2?param='+response);returnnewPromise;}).then(function(response){//输出第二次请求的结果console.log(response);}).catch(function(error){console.log(error);});JavaCompletableFuture可以实现多个CompletableFuture链调用:publicstaticvoidmain(String[]args)throwsInterruptedException{HttpClientclient=HttpClient.newBuilder().build();HttpRequestrequest=HttpRequest.newBuilder().uri(URI.create("http://foo.com/")).build();CompletableFuture>responseFuture=client.sendAsync(request,HttpResponse.BodyHandlers.ofString());responseFuture.thenCompose(response->{//把第一个请求的结果作为第二个请求的参数HttpRequestrequest2=HttpRequest.newBuilder().uri(URI.create("http://foo.com/?param="+response.body())).build();//这里必须返回一个新的CompletableFuture作为下一个的结果thenCompletableFuture>responseFuture2=client.sendAsync(request2,HttpResponse.BodyHandlers.ofString());返回响应未来2;}).thenAccept(response->{//输出第二个请求ResultSystem.out.println(response);}).exceptionally(e->{e.printStackTrace();returnnull;});//防止主线程结束后程序停止Thread.sleep(Integer.MAX_VALUE);}工具方法Promise中的工具方法:Promise.all()用于将多个Promise包装成一个新的Promise,相当于让所有的任务同时执行。当所有任务都成功时,新的Promise就会成功,只要有一个任务失败,新的Promise就会失败//同时执行3个异步任务constallPromise=Promise.all([promise1,promise2,promise3]);allPromise.then(([result1,result2,result3])=>{//3个异步任务全部成功后,可以在这里获取所有任务的结果}).catch(err=>{//只要一个任务失败,最后的结果是失败});Promise.race()用于允许同时执行多个任务。只要一个任务完成(无论成功还是失败),返回的新Promise都会跟随状态变化//发起一个HTTP请求,5秒内没有响应则超时失败Promise.race([httpGet('http://example.com/file.txt'),delay(5000).then(function(){thrownewError('timeout');})])CompletableFuture中也提供了上述类似的静态方法:staticCompletableFutureallOf(CompletableFuture>...cfs)staticCompletableFuture