摘要:出處你所必須掌握的三種異步編程方法前言都知道,語言運(yùn)行環(huán)境是單線程的,這意味著任何兩行代碼都不能同時運(yùn)行。這也是一種常見的異步編程方式,如設(shè)計(jì)模式中將這種方式稱之為命令模式。這就是我們要介紹的異步編程方法。
出處:你所必須掌握的三種異步編程方法callbacks,listeners,promise
前言coder都知道,javascript語言運(yùn)行環(huán)境是單線程的,這意味著任何兩行代碼都不能同時運(yùn)行。多任務(wù)同時進(jìn)行時,實(shí)質(zhì)上形成了一個隊(duì)列,當(dāng)隊(duì)列中前一個事件結(jié)束時,才執(zhí)行下一個事件。 如果隊(duì)列中任何一個事務(wù)費(fèi)時太長,則會造成瀏覽器假死,阻塞其他事務(wù)正常進(jìn)行,影響用戶體驗(yàn)。
js中將任務(wù)執(zhí)行分為同步模式和異步模式,上面一種即為同步模式,任何比較花時間的代碼最好設(shè)計(jì)成異步模式。通過異步編程方式,即可以達(dá)到偽多進(jìn)程。常見的異步模式主要有callbaks,listeners(一種觀察者模式),promise,下面我將會詳細(xì)描述三者各自的優(yōu)劣。
Callbacks這是再常見不過的異步編程方式了,任何前端都寫過這樣的代碼吧:
function callback(){ console.log("do callbacks"); }; setTimeout(callback,300);
通過這種方式,你可以簡單的實(shí)現(xiàn)異步編程。這里需要注意,這里的300ms只是理想時間,實(shí)際中各瀏覽器略有差異,主要取決于線程中其他任務(wù)所用的時間。這個不具體討論,其中還涉及requestAnimationFrame等。自己可以google。
此方法缺點(diǎn)是什么想必我不說你也知道,如:
function fn1(f){
alert(1);
f();
};
function fn2(f){
alert(2);
f();
};
function fn3(){
alert(3);
};
fn1(function(){ fn2(fn3); });
如果邏輯在復(fù)雜一點(diǎn)呢?恐怖看代碼的人會一頭霧水吧。而promise則可以把這種復(fù)雜的嵌套寫的一看就明白,下面會講到。
Listeners這也是一種常見的異步編程方式,如:
$elem.on("click",doClick); function doClick(){ //do something };
設(shè)計(jì)模式中將這種方式稱之為命令模式。這種方式的好處是你可以注冊多個事件,待事件觸發(fā)時調(diào)用注冊的函數(shù)。
當(dāng)然這里不僅僅是這種簡單的監(jiān)聽方式,我需要將的是一種更抽象的異步編程方式——listeners。通過觀察者模式,我們用一個數(shù)組來保存事件名稱以及事件綁定的函數(shù)方法,當(dāng)需要觸發(fā)該事件時,我們從數(shù)組中取出之前的注冊的函數(shù)。而EventProxy就是這種方式實(shí)現(xiàn)的,通過on,all注冊,emit觸發(fā)。如:
var ep = EventProxy.create("template", "data", "l10n", function (template, data, l10n) { _.template(template, data, l10n); }); $.get("template", function (template) { // something ep.emit("template", template); }); $.get("data", function (data) { // something ep.emit("data", data); }); $.get("l10n", function (l10n) { // something ep.emit("l10n", l10n); });
下面我用自己的代碼簡單的實(shí)現(xiàn)這種方式
var Observer = { _callbacks:{}, register:function(name,fn,context){ if(!name){ return; } fn = fn || function(){}; if(!Observer._callbacks[name]){ var list = Observer._callbacks[name] = []; } list.push({fn:fn,context:context}); }, trigger:function(name){ var lists = []; if(!name){ //觸發(fā)所有 for(var key in Observer._callbacks){ lists.concat(Observer._callbacks[key]); } }else{ //觸發(fā)指定 lists = Observer._callbacks[name]; } for(var i = 0,len =lists.length;i這種模式在于跨模塊異步編程中顯得尤為重要。具體的實(shí)現(xiàn)邏輯剛才已經(jīng)講過,就不具體展開,看代碼注釋即可。
Promise最后一種方法隆重登場,也是現(xiàn)在討論最多的異步編程方式。
代碼其實(shí)是現(xiàn)實(shí)的歸納總結(jié)并以另外一種方式展現(xiàn),我們有時候看不懂,是因?yàn)閷懘a方式跟現(xiàn)實(shí)有差異。假如代碼邏輯這么寫:
img1.callThisIfLoadedOrWhenLoaded(function() { // 加載完成 }).orIfFailedCallThis(function() { // 加載失敗 }); // 以及…… whenAllTheseHaveLoaded([img1, img2]).callThis(function(){ // 全部加載完成 }).orIfSomeFailedCallThis(function(){ // 一個或多個加載失敗 });我相信非程序員也能看懂吧。這就是我們要介紹的Promise異步編程方法。現(xiàn)有一些插件庫Q.js, ?when.js ?,wind.js ?,rsvp.js都是類似實(shí)現(xiàn)的,遵守一個通用的、標(biāo)準(zhǔn)化的規(guī)范:Promises/A+,jQuery 有個類似的方法叫?Deferred,但不兼容 Promises/A+ 規(guī)范,于是會有點(diǎn)小問題,使用需謹(jǐn)慎。jQuery 還有一個?Promise 類型,它其實(shí)是 Deferred 的縮減版,所以也有同樣問題。jQuery相關(guān)的后續(xù)我會分章節(jié)逐一講述,這里不做展開。
Promise異步編程總結(jié)起來就是實(shí)現(xiàn)了thenable,說的更直白的一點(diǎn)就是我跟你是好朋友,我可能有事需要你幫忙,你隨時候著吧,有需要我會第一時間通知你。你跟小王也是好哥們,并同樣告訴你需要他幫忙,并第一時間通知他。當(dāng)然你通知他是在我通知你以后。用偽代碼表示:
myself(function(){ callXiaoLi(); }) .then(function(){ //我是小李,你喊我了,我就去喊小王 callXiaowang(); }) .then(function(){ console.log("我是小王"); });同樣,下面我們會用簡單的代碼實(shí)現(xiàn)以上邏輯。
/** * author by 素人漁夫 * email:dengxiaoming1217@gmail.com * description: 異步編程promise實(shí)現(xiàn) */ (function() { /* * promise */ function EasyPromise() { this._callbacks = []; }; EasyPromise.prototype = { then: function(func, context) { var p; if (this._isdone) { p = func.apply(context, this.result); } else { p = new EasyPromise(); this._callbacks.push(function() { //此時apply context很重要,否則,res中的實(shí)例獲取不到this var res = func.apply(context, arguments); if (res && typeof res.then === "function") { res.then(p.resolve, p); }else{ p.resolve(res); }; }); } return p; }, resolve: function() { this.result = arguments; this._isdone = true; for (var i = 0; i < this._callbacks.length; i++) { this._callbacks[i].apply(this.result, arguments); } this._callbacks = []; } }; EasyPromise.when = function() { var AllPromises = [].slice.call(arguments, 0)[0]; var p = new EasyPromise(); var promiseLen = AllPromises.length; var doneParams = {}; AllPromises.forEach(function(item, index) { item.__promise__name = index; doneParams[index] = {}; item.then(checkAllPromiseDone, item); }); var donePromiseCount = 0; function checkAllPromiseDone(data) { donePromiseCount++; doneParams[this.__promise__name] = data; if (donePromiseCount == promiseLen) { doneParams = makeArray(doneParams); p.resolve(doneParams); } }; return p; }; if (typeof define !== "undefined") { define([], function() { return EasyPromise; }); } else if (typeof exports !== "undefined") { exports.EasyPromise = EasyPromise; } else { window.EasyPromise = EasyPromise; } })();then實(shí)現(xiàn)比較繞,需要解釋一下,如:.then(xx).then()
每次.then時會生成一個新的promise對象, 就是說后一個then是注冊給上一個promise對象的,其出發(fā)條件依賴于上一個.then時生成的promise。
如果上一個.then 最后返回結(jié)果是非promise對象(即沒有then方法),則直接 resolve
如果上一個.then最后返回的結(jié)果是promise對象,那.then生成的promise必須依賴返回結(jié)果中的promise,及內(nèi)層的 promise.then( ".then時生成的promise".resolve)
可能我這么解釋還是沒能解釋很清楚,自己去理解吧。
另外,谷歌瀏覽器已經(jīng)實(shí)現(xiàn)了Promise API。我使用的式chrome?版本 31.0.1650.63。不行你可以在控制臺輸入以下代碼測試:
var p = new Promise(function(resolve,reject){ setTimeout(resolve,100); setTimeout(reject,300); }); p.then(function(){ alert("resolve"); },function(){ alert("reject"); });好了,異步編程的方式就講這么多,我這里只不過是拋磚引玉,并沒有詳細(xì)闡述其api,而是以自己的代碼簡單實(shí)現(xiàn)其原來。這些異步編程方式不僅僅限于瀏覽器環(huán)境,服務(wù)器環(huán)境如nodejs也可使用。比如EventProxy本來就是在nodejs中發(fā)展而來的。
希望通過我的簡單講述能給大家提供幫忙。如果有錯,請幫忙指出,謝謝。
參考:1.http://www.html5rocks.com/en/tutorials/es6/promises/
2.http://sporto.github.io/blog/2012/12/09/callbacks-listeners-promises/
3.http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
4.http://javascript.info/tutorial/events-and-timing-depth
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/91488.html
摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:前端日報精選深入理解綁定請使用千位分隔符逗號表示網(wǎng)頁中的大數(shù)字跨頁面通信的各種姿勢你所不知道的濾鏡技巧與細(xì)節(jié)代碼質(zhì)量管控復(fù)雜度檢測中文翻譯基于與的三種代碼分割范式掘金系列如何構(gòu)建應(yīng)用程序冷星的前端雜貨鋪第期美團(tuán)旅行前端技術(shù)體系 2017-09-16 前端日報 精選 深入理解 js this 綁定請使用千位分隔符(逗號)表示web網(wǎng)頁中的大數(shù)字跨頁面通信的各種姿勢你所不知道的 CSS 濾...
摘要:解決異步編程有兩種主要方式事件模型和回調(diào)函數(shù)。將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套回調(diào)函數(shù)。方法是的別名,相當(dāng)于函數(shù)的第一個參數(shù)傳入,第二個參數(shù)傳入發(fā)生錯誤時的回調(diào)函數(shù)。 JavaScript 解決異步編程有兩種主要方式:事件模型和回調(diào)函數(shù)。但是隨著業(yè)務(wù)越來越復(fù)雜,這兩種方式已經(jīng)不能滿足開發(fā)者的需求了,Promise 可以解決這方面的問題。為了更好的理解 Promise ...
摘要:為了降低異步編程的復(fù)雜性,所以。難理解請參考的誤區(qū)以及實(shí)踐異步編程的模式異步編程的種方法 異步編程 javascript異步編程, web2.0時代比較熱門的編程方式,我們平時碼的時候也或多或少用到,最典型的就是異步ajax,發(fā)送異步請求,綁定回調(diào)函數(shù),請求響應(yīng)之后調(diào)用指定的回調(diào)函數(shù),沒有阻塞其他代碼的執(zhí)行。還有像setTimeout方法同樣也是異步執(zhí)行回調(diào)的方法。 如果對異步編程...
摘要:接下來介紹下異步編程六種方法。六生成器函數(shù)是提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同,最大的特點(diǎn)就是可以控制函數(shù)的執(zhí)行。參考文章前端面試之道異步編程的種方法你不知道的中卷函數(shù)的含義和用法替代的個理由 前言 我們知道Javascript語言的執(zhí)行環(huán)境是單線程。也就是指一次只能完成一件任務(wù)。如果有多個任務(wù),就必須排隊(duì),前面一個任務(wù)完成,再執(zhí)行后面一個任務(wù)。 這種模式雖然實(shí)現(xiàn)起...
閱讀 2538·2023-04-26 00:57
閱讀 911·2021-11-25 09:43
閱讀 2221·2021-11-11 16:55
閱讀 2207·2019-08-30 15:53
閱讀 3592·2019-08-30 15:52
閱讀 1459·2019-08-30 14:10
閱讀 3379·2019-08-30 13:22
閱讀 1209·2019-08-29 11:18