Organizando JavaScript (ou CoffeeScript, notas de SuperheroJS.com)

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)

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?