《JavaScript 标准参考教程(alpha)》by阮一峰
ES6原生提供了Promise对象。能够将异步操做以同步操做的流程表达出来,避免了层层嵌套的回调函数。 javascript
javascript语言的执行环境是单线程的,即一次只能执行一个任务,若是有多个任务,就必须等待前面的任务执行完后才能执行下一个任务,若是前一个任务耗时很长,就会陷入阻塞,浏览器呈“假死”状态。好比,发送一个请求,等待请求返回的过程当中这段时间就没有执行任何一个任务,白白占用了进程。php
针对这种状况,javascript提出了同步和异步两种模式。html
即传统做法,每一个任务按顺序执行,程序的执行顺序和任务的排列顺序是一致的。java
每个任务分红两段,第一段代码包含对外部数据的请求,第二段代码被写成一个回调函数,包含了对外部数据的处理。jquery
第一段代码执行完,不是马上执行第二段代码,而是将程序的执行权交给第二个任务。等到外部数据返回了,再由系统通知执行第二段代码。ajax
因此,程序的执行顺序与任务的排列顺序是不一致的、异步的。编程
var f2 = function(){ console.log('f2'); } var f1 = function(callback) { console.log('f1'); setTimeout(function(){ callback(); },1000); } f1(f2); console.log('外部');
结果:
f1
外部
f2json
优势:简单、容易理解和部署
缺点:不利于代码的阅读和维护,各个部分之间高度耦合,使程序结构混乱,流程难以追踪,并且每一个任务只能指定一个回调函数promise
使用jquery来举例浏览器
f1.on('done', f2); function f1(){ setTimeout(function () { // f1的任务代码 f1.trigger('done'); //f1的代码执行完成后当即执行done事件 }, 1000); }
优势:比较容易理解,能够绑定多个事件,每一个事件能够指定多个回调函数,并且能够”去耦合“,有利于实现模块化。
缺点:整个程序都要变成事件驱动型,运行流程会变得很不清晰。
“事件”能够理解成”信号”,若是存在一个”信号中心”,某个任务执行完成,就向信号中心”发布”一个信号,其余任务能够向信号中心”订阅”(subscribe)这个信号,从而知道何时本身能够开始执行。这就叫作”发布/订阅模式“,又称”观察者模式“。
具体方法略。可以使用jquery的插件
咱们提到异步模式的程序执行顺序与任务排列顺序并不肯定是否一致,取决于向外部请求数据所耗费的时间长短。为了能控制顺序,有如下的方法:
ps:下面说的串行和并行针对的是对于异步任务的执行顺序,而上面说的同步、异步指的是异步仍是同步
function async(item, callback){ setTimeout(function(){ callback(item); },1000); } function final() { console.log(results); } var items = [ 1, 2, 3, 4, 5, 6 ]; var results = []; function series(item) { if(item) { async( item, function(result) { results.push(result); return series(items.shift()); }); } else { return final(results); } } series(items.shift());
结果:
[1, 2, 3, 4, 5, 6]async
就是咱们的异步任务,从结果中能够看出来,的确按照了咱们的任务排列顺序执行了。也能够看做把异步任务按同步模式来执行。
var a=1; //用来标记异步任务第一部分的执行顺序 var b=1; //用来标记异步任务第二部分的执行顺序 function async(item, callback){ console.log(a); a++; setTimeout(function(){ console.log('回调任务',b); b++; callback(item); },2000); } function final() { console.log(results); } var items = [ 1, 2, 3, 4, 5, 6 ]; var results = []; items.forEach(function(item) { async(item, function(result){ results.push(result); if(results.length == items.length) { final(); } }) });
结果:
1
2
3
4
5
6
回调任务1
回调任务2
回调任务3
回调任务4
回调任务5
回调任务6
[1,2,3,4,5,6]
异步执行的意思是,所有任务同时进行,等全部异步任务都执行完后才执行final
函数。
但这样虽然加快了速度,却存在任务过多而形成资源消耗大,运行速度过慢的问题,所以咱们须要控制一次同时执行任务的个数,也就是串行和并行相结合。
var items = [ 1, 2, 3, 4, 5, 6 ]; var results = []; var running = 0; var limit = 2; function launcher() { while(running < limit && items.length > 0) { var item = items.shift(); async(item, function(result) { results.push(result); running--; if(items.length > 0) { launcher(); } else if(running == 0) { final(); } }); running++; } } launcher();
为了用更简洁的代码实现上述异步编程的执行顺序,ES6就推出了promise对象
三种状态:未完成、已完成、失败
最终结果:成功——>传回一个值;失败——>抛出一个错误
var promise = new Promise(function(resolve, reject) { // 异步操做的代码 if (/* 异步操做成功 */){ resolve(value); } else { reject(error); } });
var po = new Promise(); po.then(function(value) { // success }, function(value) { // failure }); 若是省略then的第二个参数,则仅表明成功后的回调函数。
po.then(step1).then(step2).then(step3)
链式使用,一旦前面有一步错误,后面都不会执行
$.ajax({ url: 'test.php', //请求地址 data: {'v': v}, //发送的数据 dataType: 'json',//返回的数据格式 type: 'get', //请求的方式:get|post //发送前执行 beforeSend: function() { console.log('beforeSend'); }, //返回成功执行 success: function(result) { console.log(result); }, //返回失败执行 error: function(err) { console.log(err); }, //不管成功仍是失败都会执行 complete: function() { console.log('complete'); }, //状态码对应的操做 statusCode: { 200: function() {}, 404: function() {} } }) 结合实际选择须要的选项
$.ajax({url: 'test.php'}) .success(function() {}) .error(function() {}) .success(function() {})
$.ajax()返回的就是promise对象
1.8版本后,jquery使用done(),fail()和always()分别对应着前面的三个方法
$.ajax({url: 'test.php'}) .done(function() {}) .fail(function() {}) .always(function() {}) 能够用then()方法把done()和fail()合并在一块儿 promise.then(function() { //done }, function() { //fail }) 若是只有一个参数,就表示done()方法
$.when(deferreds):提供一种方法来执行一个或多个对象的回调函数,Deferred(延迟)对象一般表示异步事件
$.when( $.ajax({url: 'test.php'}) ) .then(function(data, status, jqXHR) {}) $.when( $.ajax({url:'test.php'}), $.ajax({url:'test1.php'}) ) .done(function(result, result1){ console.log(result); console.log(result1); }) .fail(function(err){ console.log("error"); }) 当两个ajax都执行成功时就会调用done方法,不然只要其中一个失败就会执行fail方法。