回调地狱javascript
Javascript 语言的执行环境是“单线程”(single thread)。所谓“单线程”,就是指一次只能完成一件任务。若是有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。html
这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),每每就是由于某一段 JavaScript 代码长时间运行(好比死循环),致使整个页面卡在这个地方,其余任务没法执行。java
为了解决这个问题,Javascript 语言将任务的执行模式分红两种:同步(Synchronous)和异步(Asynchronous)。数组
假定有两个函数f1和f2,后者必须等到前者执行完成,才能执行。这时,能够考虑改写f1,把f2写成f1的回调函数。promise
function f1(callback) { callback(); }
f1.on('done', f2); function f1(){ setTimeout(function () { // f1的任务代码 f1.trigger('done'); }, 1000); }
jQuery.subscribe("done", f2); function f1(){ setTimeout(function () { // f1的任务代码 jQuery.publish("done"); }, 1000); } jQuery.unsubscribe("done", f2);
这种方法的性质与”事件监听”相似,可是明显优于后者。由于咱们能够经过查看”消息中心”,了解存在多少信号、每一个信号有多少订阅者,从而监控程序的运行。浏览器
若是有多个异步操做,就存在一个流程控制的问题:肯定操做执行的顺序,之后如何保证遵照这种顺序异步
function async(arg, callback) { console.log('参数为 ' + arg +' , 1秒后返回结果'); setTimeout(function() { callback(arg * 2); }, 1000); }
上面代码的async函数是一个异步任务,很是耗时,每次执行须要1秒才能完成,而后再调用回调函数。async
若是有6个这样的异步任务,须要所有完成后,才能执行下一步的final函数。函数
function final(value) { console.log('完成: ', value); }
请问应该如何安排操做流程?spa
async(1, function(value){ async(value, function(value){ async(value, function(value){ async(value, function(value){ async(value, function(value){ async(value, final); }); }); }); }); });
上面代码采用6个回调函数的嵌套,不只写起来麻烦,容易出错,并且难以维护
咱们能够编写一个流程控制函数,让它来控制异步任务,一个任务完成之后,再执行另外一个。这就叫串行执行。(任务队列)
let taskQueen = [1, 2, 3, 4, 5, 6]; let result = []; function invoke(curTask) { if (curTask) { console.log('当前正在执行任务', curTask); result.push(curTask + '完成'); } else { console.log('当前任务所有完成'); } } invoke(taskQueen.shift());
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());
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(results); } }) });
上面代码中,forEach方法会同时发起6个异步任务,等到它们所有完成之后,才会执行final函数。
并行执行的好处是效率较高,比起串行执行一次只能执行一个任务,较为节约时间。可是问题在于若是并行的任务较多,很容易耗尽系统资源,拖慢运行速度。所以有了第三种流程控制方式
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(results); } }); running++; } } launcher();
Promise 对象用于一个异步操做的最终完成(或失败)及其结果值的表示。(简单点说就是处理异步请求。咱们常常会作些承诺,若是我赢了你就嫁给我,若是输了我就嫁给你之类的诺言。这就是promise的中文含义:诺言,一个成功,一个失败。) -MDN
new Promise( /* executor */ function(resolve, reject) {...} );
一个 Promise有如下几种状态:
var promise = new Promise(function(resolve, reject){ resolve("传递给then的值"); }); promise.then(function (value) { console.log(value); }, function (error) { console.error(error); });
捕获promise 运行的各类错误 promise.then(undefined, onRejected)
的语法糖
var promise = new Promise(function(resolve, reject){ resolve("传递给then的值"); }); promise.then(function (value) { console.log(value); }).catch(function (error) { console.error(error); });
生成并返回一个新的promise对象。
参数传递promise数组中全部的promise对象都变为resolve的时候,该方法才会返回, 新建立的promise则会使用这些promise的值。
若是参数中的任何一个promise为reject的话,则整个Promise.all调用会当即终止,并返回一个reject的新的promise对象。
因为参数数组中的每一个元素都是由 Promise.resolve 包装(wrap)的,因此Paomise.all能够处理不一样类型的promose对象。
var p1 = Promise.resolve(1), p2 = Promise.resolve(2), p3 = Promise.resolve(3); Promise.all([p1, p2, p3]).then(function (results) { console.log(results); // [1, 2, 3] });
var p1 = Promise.resolve(1), p2 = Promise.resolve(2), p3 = Promise.resolve(3); Promise.race([p1, p2, p3]).then(function (value) { console.log(value); // 1 });
生成并返回一个新的promise对象。
参数 promise 数组中的任何一个promise对象若是变为resolve或者reject的话, 该函数就会返回,并使用这个promise对象的值进行resolve或者reject。
promise阮一峰(http://javascript.ruanyifeng....