Notas de SuperheroJS / Organizing Your Code
Esboço
- Projetando uma API JavaScript melhor
- Interfaces fluentes: encadeamento de métodos
- Manipulando Argumentos (Reduzindo a repetição com mapas, sendo flexível com os tipos)
- JS assíncrono
- Callbacks
- Eventos
- Bibliotecas de fluxo de controle (AsyncJS)
- Promessas
Projetando uma API JavaScript melhor
“Os desenvolvedores de APIs melhores vão adorar “
Referência de: Designing Better JavaScript APIs (Smashing Magazine)
Interface Fluente
Encadeamento de Método
Muito usado em jQuery
$elem = $("#elemId")
$elem.css("background", "orange")
$elem.on("click", doSomething)
$elem.on("keypress", doAnother)
# vs
$("#elemId").css("background", "orange")
.on("click", doSomething)
.on("keypress", doAnother)Outra possibilidade (interface fluente semelhante a SQL imaginária)
query = select(column1, column2)
.from(tableName)
.where("column", EQUALS, "x")
// since this is not a string, u can modify parts out of order
query.join(table2)
...
// finally get the string
query.toString()- [Um exemplo em JSFiddle] [fidd le-fluent-sql]
Vantagens
- Melhor legibilidade
- Menor variável de repetição / acesso
- Em alguns casos, permite mais flexibilidade (como no exemplo da API SQL fludente)
Separação de consulta de comando (CQS)
- Os comandos mudam de estado (por exemplo, setters)
- Consultar valores de recuperação (por exemplo, getters)
- Mas interfaces fluentes retornam um objeto self …
Então CQS foi quebrado deliberadamente
// getter
$elem.css("background")
// setter
$elem.css("background", "red")
Tornar-se fluente estendendo prototype
// more fluent
Array::map = someFunction
Array::reduce = anotherFunction
// - example usage
arr.map(doSomething)
// less fluent
_.map = someFunction
_.reduce = anotherFunction
// - example usage
_.map(arr, doSomething)
- Subjetivo
- Estender protótipos pode causar comportamento inesperado
- Conflitos: e se a função já estiver definida
- E se as implementações (argumentos / IO) forem diferentes
- Feito por algumas bibliotecas: sugar.js , date.js
- ” Com grande poder, vem grande responsabilidade “: Use com cuidado
Manipulando Argumentos
Procure eliminar a repetição (por exemplo, use um mapa)
$elem.css("background", "red")
.css("color", "white")
.css("font-weight", "bold")
// vs
$elem.css({
"background": "red",
"color": "white",
"font-weight": "bold"
})
Ser flexível com os tipos
Aceite vários tipos de entrada conforme aplicável
myDateObj.until(1370759029735)
myDateObj.until("2013-02-12")
myDateObj.until(new Date(2013, 06, 10))
Argumentos nomeados e opcionais usando mapas
something = (x1, x2, x3, x4, ...) ->
if x1 is undefined then x1 = someDefault
if x2 is undefined then x2 = someDefault
...
// vs
something = (opts) ->
defaults = {
prop1 = "x",
prop2 = "y",
prop3 = "z"
}
options = $.extend({}, defaults, opts)
JavaScript assíncrono
- Callbacks
- Eventos
- Bibliotecas de fluxo de controle
- Promessas
Callbacks
foo = (doneCallback, errorCallback) ->
// fazer algo
if success
if _.isFunction(doneCallback) then doneCallback(value)
else
if _.isFunction(errorCallback) then errorCallback(err)
Vantagens
- Padrão bem conhecido
- Muito fácil de implementar
Desvantagens
- Callbacks altamente aninhados (pirâmide da desgraça): difícil de ler e testar
- Apenas 1 retorno de chamada por evento
Eventos
Normalmente usado em aplicativos GUI. Amplamente utilizado em jQuery e BackboneJS
vent.trigger("someEvent")
// objects can listen to this and handle it
vent.on("someEvent", doSomething)
Vantagens
- Padrão bem compreendido
- Muitos ouvintes de um objeto
Desvantagens
- Um pouco mais difícil de implementar do zero. Mas com bibliotecas como jQuery / Backbone, é muito fácil
Tornando-se assíncrono
Usando uma biblioteca de fluxo de controle ( AsyncJS )
Vantagens
- Mais fácil de entender – pode visualizar o fluxo. de cima para baixo
Desvantagens
- As funções podem precisar ser adaptadas para uso com AsyncJS (por exemplo, adicionar callbacks concluídos)
Use adiamentos / promessas
aLongTask = ->
deferred = $.Deferred()
(doSomething ->
// a long task
if success
deferred.resolve(value)
else
deferred.reject(err)
)()
return deferred
aLongTask().done(handleSucess)
.fail(handleFailure)
- jQuery adiou
- Muitas outras bibliotecas que fornecem essa funcionalidade
- Útil saber
- AsyncJS : e se você quiser esperar que várias funções assíncronas terminem. Ou mesmo apenas alguns?
- Ou mesmo ParallelJS , verdadeiro multi-threading usando HTML5 Web Workers
Vantagens
- Mais poderoso
- Pode ser agregado, distribuído, adicionar ouvintes quando já resolvido
Desvantagens
- Menos entendido?
- Difícil de acompanhar com muitas promessas agregadas com ouvintes adicionados ao longo do caminho?