回調地獄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....