在JavaScript世界中,所有代码都由一个线程执行。
由于这个“缺陷”,JavaScript和浏览器事件的所有网络操作都必须异步执行。异步执行可以用回调函数来实现:
函数回调(){ console . log(' Done ');}console.log('在setTimeout()之前');setTimeout(回调,1000);//1秒后调用回调函数console . log(' after setTimeout()');观察上述代码的执行,您可以在Chrome的控制台输出中看到:
settimeout()之前settimeout()之后(等待1秒后)完成
可以看出,异步操作将在未来的某个时刻触发函数调用。AJAX是典型的异步操作。以上一节中的代码为例:
request . onreadystatechange=function(){ if(request . readystate===4){ if(request . status===200){ return success(request . responsetext);} else { return fail(request . status);}}}将回调函数success(request.responseText)和fail(request.status)写入AJAX操作是正常的,但是不好看,不利于代码重用。
有没有更好的写法?例如,这样写:
var ajax=ajaxGet('http://.));ajax.ifSuccess(成功)。ifFail(失败);这种链式编写的优点是,AJAX逻辑一开始是统一执行的,它不关心如何处理结果。然后,根据结果是成功还是失败,在未来的某个时间将调用成功函数或失败函数。
古人说“君子一言,驷马难追”,这个“承诺将来要执行”的对象在JavaScript中被称为承诺对象。
Promise有各种开源实现,在ES6中是标准化的,浏览器直接支持。首先测试您的浏览器是否支持Promise:
使用“严格”;new Promise(函数(){ });//直接运行测试:alert('支持Promise!');我们先来看看Promise最简单的例子:生成一个0到2之间的随机数。如果小于1,一段时间后返回成功,否则返回失败:
函数测试(解析,拒绝){ var time out=math . random()* 2;日志('将超时设置为: '超时'秒');setTimeout(函数(){ if(TiME 1){ log(' call resolve(). '));解决(' 200 OK ');} else { log('call reject(). ');拒绝(‘超时’秒内的超时));} },TiME * 1000);}这个test()函数有两个参数,都是函数。如果执行成功,我们将调用resolve(‘200 OK’);如果执行失败,我们将调用reject(“超时”秒中的“超时”)).可以看出,test()函数只关心自己的逻辑,并不关心具体的解析和拒绝会如何处理结果。有了执行函数,我们可以使用Promise对象来执行它,并在未来的某个时候获得成功或失败:
var p1=新承诺(测试);var p2=P1 . then(function(result){ console . log(' success:' result ');});Varp3=p2.catch(函数(原因){ console . log(' failure:' reason ');});变量p1是一个Promise对象,负责执行测试函数。因为测试函数在内部异步执行,当测试函数成功执行时,我们告诉Promise对象:
//如果成功,执行这个函数:P1 . then(function(result){ console . log(' success:' result ');});当测试函数无法执行时,我们告诉Promise对象:
P2。catch(function(reason)){ console . log(' failure:' reason ');});Promise对象可以连接在一起,因此上面的代码可以简化为:
新承诺(测试)。然后(函数(结果){控制台。日志(‘成功’:结果);}).catch(function(reason)){ console . log(' failure:' reason ');});给它一个真实的测试,看看Promise是如何异步执行的:
使用"严格";//清除日志: var日志=文档。getelementbyid(' test-promise-log ');登录时。孩子们。长度1){记录。删除子项(登录。孩子们[登录。孩子们。length-1]);}//输出原木到页面:函数日志{ var p=文档。创建元素(' p ');p . innerHTMl=s;日志记录。append child(p);}新承诺(功能(解决,拒绝){日志('开始新承诺.');var TiME=Math。random()* 2;日志('将超时设置为: '超时'秒');setTimeout(函数(){ if(TiME 1){ log(' call resolve(). '));解决(' 200 OK ');} else { log('call reject(). ');拒绝('超时'秒内的超时));} },TiME * 1000);}).然后(函数{ log(' Done : ' r);}).捕捉(函数(原因){日志(' Failed: '原因);});Log:
开始新的承诺.将超时设置为:秒。调用解析().Done: 200 OK
可见承诺最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了:
承诺还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。要串行执行这样的异步任务,不用承诺需要写一层一层的嵌套代码。有了答应我,我们只需要简单地写:
工作1。然后(工作2).然后(工作3)。catch(handleError);其中工作1、工作2和工作3都是承诺对象。下面的例子演示了如何串行执行一系列需要异步计算获得结果的任务:
使用"严格";var日志=文档。getelementbyid(' test-promise 2-log ');登录时。孩子们。长度1){记录。删除子项(登录。孩子们[登录。孩子们。length-1]);}个函数日志{ var p=文档。创建元素(' p ');p . innerHTMl=s;日志记录。append child(p);}//0.5秒后返回输入*输入的计算结果:函数乘法(输入){返回新的承诺(函数(解析,拒绝){ log('计算'输入x '输入'.');setTimeout(解析,500,输入*输入);});}//0.5秒后返回输入输入的计算结果:函数添加(输入){返回新的承诺(函数(解析,拒绝){日志('计算'输入' '输入'.');setTimeout(解析,500,输入输入);});}var p=新承诺(函数(解析,拒绝){ log('开始新承诺.');决心(123);});然后相乘。然后(添加)。然后(相乘)。然后(添加)。然后(函数(结果){ log('获取值: '结果);});Log:
开始新的承诺.计算123 x 123.计算15129 15129.计算30258 x 30258.计算915546564 915546564.获得价值: 1831093128
setTimeout可以看成一个模拟网络等异步执行的函数。现在,我们把上一节的创建交互式、快速动态网页应用的网页开发技术异步执行函数转换为承诺对象,看看用承诺如何简化异步处理:
使用"严格";//ajax函数将返回承诺对象:函数ajax(方法、网址、数据){ var request=new XMlhttprequest();返回新的承诺(函数(解析,拒绝){请求。onreadystatechange=function(){ if(request。readystate===4){ if(请求。status===200){ resolve(请求。response text);} else { reject(请求。地位);} } };request.open(方法,网址);request.send(数据);});} var日志=文档。getelementbyid(' test-promise-Ajax-result ');var p=ajax('GET ','/API/categories ');p.then(function (text) { //如果创建交互式、快速动态网页应用的网页开发技术成功,获得响应内容log.innerText=text})。捕捉(函数(状态){ //如果创建交互式、快速动态网页应用的网页开发技术失败,获得响应代码log.innerText='ERROR: '状态;});除了串行执行若干异步任务外答应我还可以并行执行异步任务。试想一个页面聊天系统,我们需要从两个不同的统一资源定位器分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用承诺实现如下:
var p1=新Promise(函数(解析,拒绝){ setTimeout(解析,500,' P1 ');});var p2=新Promise(函数(解析,拒绝){ setTimeout(解析,600,' P2 ');});//同时执行p1和p2,然后执行: promise.all ([P1,p2])。然后(函数(结果){console.log(结果);//获取Array: ['P1 ',' P2 ']});有时,多个异步任务是为了容错。例如,要同时从两个网址读取用户的个人信息,只需要先得到返回的结果。在这种情况下,使用Promise.race()实现:
var p1=新Promise(函数(解析,拒绝){ setTimeout(解析,500,' P1 ');});var p2=新Promise(函数(解析,拒绝){ setTimeout(解析,600,' P2 ');});Promise.race([p1,p2])。然后(函数(结果){ console.log(结果);//' P1 ' });由于p1执行得更快,因此Promise的()将得到结果“P1”。P2仍在执行,但执行结果将被丢弃。
如果我们组合使用Promise,我们可以并行和串行组合许多异步任务。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。