2012-05-15 1 views
0

У меня есть веб-сервиса, который может принимать определенные параметры, как таковой,? Наверху = 5 & OrderBy = столбец --- и т.д ...Javascript сглаживающий метод цепочки

Я хочу, чтобы иметь возможность выполнить свой объект, как, например :

var call = new API(); 
call.get().top(5).skip(15).orderby("address"); 

Задача состоит в том, чтобы только последний порядок запуска запускал метод execute(). Вот мой код. Пожалуйста, дайте мне знать, если у вас есть идеи! В настоящее время он задерживается на 25 мс, когда заканчивается каждая функция, и останавливает таймер при запуске следующей функции. Является ли это правильным/приемлемым?


var API = function (webservice) { 
this.webservice(webservice); 
return this; 
}; 

API.prototype = { 
version: function (urlFormat) { 
    if (urlFormat) { 
     return "v" + urlFormat.split('.').join('_'); 
    } 
    return sessionStorage.getItem("version"); 
}, 
path: function() { 
    return "../WebAPI/"; 
}, 
execute: function() { 
    var path = this.path() + this.webservice() + ".svc/"; 
    if (this.__parameters) { 
     path += "?"; 
    } 
    var first = true; 
    for (var k in this.__parameters) { 
     if (k !== "type") 
     path += ((first) ? (function(){first = false; return ""})() : "&") + "$" + k + "=" + this.__parameters[k]; 
    }; 
    console.log(this.__parameters.type + ": " + path); 
    return this; 
}, 
put: function() { 
    this.doIt("type","put"); 
    return this; 
}, 
post: function() { 
    this.doIt("type","post"); 
    return this; 
}, 
get: function() { 
    this.doIt("type","get"); 
    return this; 
}, 
delete: function() { 
    this.doIt("type","delete"); 
    return this; 
}, 
toString: function() { 
    return "API"; 
}, 
webservice: function(webservice) { 
    if (webservice) { 
     this.__webservice = webservice; 
    } 
    else { 
     return this.__webservice; 
    } 
}, 
top: function (p) { 
    this.doIt("top",p); 
    return this; 
}, 
view: function (p) { 
    this.doIt("view",p); 
    return this; 
}, 
orderby: function (p) { 
    this.doIt("orderby",p); 
    return this; 
}, 
criteria: function (p) { 
    this.doIt("criteria",p); 
    return this; 
}, 
skip: function (p) { 
    this.doIt("skip",p); 
    return this; 
}, 
filter: function (p) { 
    this.doIt("filter",p); 
    return this; 
}, 
doIt: function (method, parameter) { 
    this.__timerStop(); 
    this.__parameters[method] = parameter; 
    this.__timerStart(); 
}, 
__timerStop: function() { 
    if (this.__timer) { 
     clearTimeout(this.__timer); 
    } 
}, 
__timerStart: function (append) { 
    var self = this; 
    if (this.__timer) { 
     this.__timerStop(); 
    } 
    this.__timer = setTimeout(function() { 
     console.log("executing."); 
     console.log(JSON.stringify(self.__parameters)); 
     self.execute(); 
    }, 25); 
}, 
__parameters: {} 
}; 
+1

Мне не нравится идея волшебного исполнения.Либо вы выполняете после каждого вызова метода, либо программист должен явно выполнить вызов. Я никогда не видел этого отсроченного исполнения раньше, и это дает мне крипы. – Styxxy

ответ

6

Update: Вы знаете, что? Я собираюсь смягчить свою позицию на этом (оригинальный ответ ниже). Вы должны быть в порядке, учитывая, что обратный вызов, который вы передаете в setTimeout, никогда не может быть запущен до того, как ваша цепочка методов будет «завершена» с помощью однопоточного цикла событий JavaScript. (И в самом деле, это также означает, что вы должны быть безопасным прохождением 0 к setTimeout вместо 25.)

Я все еще думает, что вы гайку придумывая эту конструкцию (и если это код, который несколько разработчиков будут трогательными, Я бы сказал, что вам лучше с более простым дизайном, чтобы уменьшить риск путаницы в команде из-за чрезмерной сложности); но если вы настаиваете на том, чтобы пройти этот путь, вы на самом деле не должны сталкиваться с любыми странными гейзенбергами.

Но да, я согласен с моим первоначальным советом о необходимости прямого вызова execute.


О, человек. Вы сумасшедший, чтобы даже подумать об этом! Я имею в виду, что часть меня действительно вас любит (я big fan of horrifying hacks); но факт заключается в том, что, принимая этот подход, в то время как он может в конечном итоге работать, приведет вас в орехи, если/когда он идет haywire.

Основная причина, я бы сильно обескуражить его в том, что альтернативой является очень легко и, что более важно, на самом деле надежным: просто установить правило, execute метод, который фактически отправляет запрос, и поэтому любой прикован вызов метода должен заканчиваться, что:

call.get().top(5).skip(15).orderby("address").execute(); 

Если вы серьезно влюблена в эту идею таймера на основе, что-то подсказывает мне, что вы действительно никогда не страдал от Heisenbug до (или, как я изначально догадывался, ты это просто из вашего ума).

+0

Кроме того, метод '' execute'' может принимать функцию обратного вызова, которая может быть передана объекту '' results'', что значительно упрощает пользователю указывать *, что они хотят сделать, когда их запрос завершен. –

+0

Для того, что стоит, вы можете уменьшить значение setTimeout до 1 (не 0 - 0 = без задержки и, следовательно, 2 запроса) - поскольку JS оценивает код «неустановочно», таким образом, 1ms является задержкой для проверки наличия в цепочке другого метода. Хотя я должен согласиться с комментарием выше - есть ли причина для отсутствия функции выполнения? – eithed

+1

@eithed: Я думаю, вы ошибаетесь в этом. См. Этот jsFiddle: http://jsfiddle.net/m3dqs/. Вы можете видеть, что даже если '0' передано' setTimeout', функция обратного вызова не выполняется синхронно; поэтому каждая итерация цикла отменяет предыдущую. –

1

Интересная идея. Хотя, почему бы не сделать что-то вроде этого, вместо:

call({ type: "get", top: 5, skip: 15, orderby: "address" }); 

Затем обработать каждый аргумент, обернув через объект внутри call реализации, а затем сделать запрос на обслуживание.

for(var arg in args) { 
    if(args.hasOwnProperty(arg) && args.propertyIsEnumerable(arg)) { 
     // process argument 
    } 
} 

Это простота вещей.

+1

очевидная причина - выполнение 'call.get(). Top()' может делать что-то другое, чем 'call.top(). Get()' - если вы передаете все атрибуты как один объект, вы не можете отличить поведение, но должен его закодировать. Пример: 'Cow.Eat ('grass'). Poo()' нечто иное, чем 'Cow.Poo(). Ешьте ('grass')', как в первом примере Cow пуст, а во второй - травой. – eithed