Sam & Max: Python, Django, Git et du cul » javascript http://sametmax.com Deux développeurs en vadrouille qui se sortent les doigts du code Wed, 05 Feb 2014 14:20:37 +0000 en hourly 1 http://wordpress.org/?v=3.3.1 Ignorer certains caractères spéciaux dans un template django http://sametmax.com/ignorer-certains-caracteres-speciaux-dans-un-template-django/ http://sametmax.com/ignorer-certains-caracteres-speciaux-dans-un-template-django/#comments Mon, 09 Dec 2013 07:04:51 +0000 Sam http://sametmax.com/?p=8268 Hier Max me demandait comment mettre un template Javascript dans un template Django s’ils utilisent la même syntaxe.

La réponse : utiliser le tag “verbatim” :

{% verbatim %}
  Mettre ici le code que django doit afficher tel quel, sans interpréter.
{% endverbatim %}

flattr this!

]]>
http://sametmax.com/ignorer-certains-caracteres-speciaux-dans-un-template-django/feed/ 6
La différence entre la programmation asynchrone, parallèle et concurrente http://sametmax.com/la-difference-entre-la-programmation-asynchrone-parallele-et-concurrente/ http://sametmax.com/la-difference-entre-la-programmation-asynchrone-parallele-et-concurrente/#comments Wed, 09 Oct 2013 22:08:13 +0000 Sam http://sametmax.com/?p=7378 On parle un peu partout de programmation non bloquante ces temps-ci. NoSQL a remis le map/reduce au goût du jour, et PAF, on vous sort le mot clé parallélisation pour vous en vendre une tetrachiée. Les partisants de NodeJS vont crier “asynchrone”, parce que c’est ce que Javascript sait faire de mieux. Et on murmure dans les coins que la robustesse d’Erlang tient dans ses acteurs qui travaillent de manière concurrente dans la VM.

Ok, donc tout ça, ça à l’air de faire la même chose, c’est à dire de faire plusieurs choses en même temps, sans bloquer.

Donc c’est pareil ?

Non. En fait c’est une question de point de vue : non bloquant dans quel contexte ?

Si c’est l’IO, c’est asynchrone

Pour rappel, l’IO (Input/Ouput), c’est toute activité qui implique que des données entrent et sortent de votre programme : saisie utilisateur, print sur un terminal, lecture sur une socket, écriture sur le disque, etc. Une opération I/O a plusieurs caractéristiques :

  • Le temps que prend l’opération n’est pas dépendant du CPU : la vitesse du disque, la latence du réseau, le nombre d’heures de sommeil du sysadmin sont les facteurs qui vont déterminer quand l’opération va prendre fin.
  • Le corollaire, c’est qu’on ne peut pas prédire quand l’opération va prendre fin depuis le programme.
  • Sur les services avec beaucoup d’I/O (serveurs Web, bases de données, crawlers, scripts de déploiement, etc), c’est l’I/O qui généralement prend le plus de temps dans l’exécution du programme. L’optimisation de ces opérations va donc l’accélérer bien plus que de changer votre algo.

La plupart des programmes bloquent quand ils effectuent une opération I/O. Par exemple, si vous faites ceci en Python :

import urllib2
 
# télécharge et affiche le contenu de la page d'acceuil de sam et max
print(urllib2.urlopen('http://sametmax.com').read())
print("Coucou")

La ligne print("Coucou") ne s’exécutera pas tant que la ligne précédente n’aura pas terminé de s’exécuter. Dans ce cas ce n’est pas très grâve, mais dans ce cas là :

import urllib2
 
 
mille_urls = obtenir_liste_de_mille_urls()
contenu = []
 
# télécharge et sauvegarde dans une liste
# le contenu de chacune des 1000 urls
for url in mille_urls:
	contenu.append(urllib2.urlopen(url).read())

Chaque url est téléchargée une par une, et comme Internet, c’est vachement lent (300 ms X 1000, ça fait 5 minutes, mine de rien), votre programme va prendre un temps fou. Et pour rien en plus, car votre programme va passer la majeure partie du temps à ne rien faire ! En effet, 99% du temps de votre programme est passé à attendre qu’Internet réponde, pendant que votre CPU se touche les noix.

La programmation asynchrone est une réponse à cela : au lieu d’attendre que se finissent les entrées et les sorties, le programme continue de fonctionner.

Une autre problématique se pose alors : comment obtenir le résultat de l’opération d’I/O, puisqu’on ne sait pas quand il va arriver et qu’on attend pas qu’il arrive ?

C’est là que les systèmes synchrones font un peu de magie. En vérité, une partie du programme attend, mais discrètement, en arrière plan, au niveau de ce qu’on appelle une boucle d’événements (“events loop”), c’est à dire une boucle infinie qui check régulièrement si une opération I/O ne s’est pas terminée.

Cette boucle est invisible pour vous, votre programme continue de tourner. Mais si une opération I/O envoie des données, alors l’events loop va réagir.

Ca a l’air compliqué, mais en fait, c’est, la plupart du temps, juste une histoire de callback (si la notion vous échappe, je vous renvois à l’article dédié…). Par exemple en Javascript :

var mille_urls = obtenir_liste_de_mille_urls();
var contenu = [];
 
# notre callback qui va permettre d'ajouter 
# le contenu téléchargé à notre liste
var callback = function(data) { 
      contenu.push(data);
};
 
# Bon, j'utilise jquery pour simplifier le code...
# On boucle sur les milles URL
$.each(mille_urls, function(index, url) {
  # On télécharge le contenu, MAIS comme
  # $.get est naturellement non blocante,
  # elle ne va pas attendre qu'internet 
  # réponde pour continuer la boucle, et
  # donc on pourra attendre plusieurs réponses
  # en même temps. Pour avoir le résultat de 
  # chaque réponse, on passe un callback qui 
  # va être appelé quand la réponse arrive.
  $.get(url, callback);
 
});

Comprenez bien la subtilité : à tout moment, il n’y a qu’UN SEUL process javascript qui s’éxécute. Il n’y a pas deux traitements, pas de threads, pas de processus parallèles, rien de tout ça. Simplement, Javascript n’attend pas la réponse de sa requête pour faire la requête suivante, il continu sur sa lancée, et donc peut optimiser les temps d’attente en attendant plusieurs choses en même temps.

Javascript utilise massivement des API asynchrones, c’est au cœur du langage, il n’y a aucun effort à faire pour cela. A l’inverse, Python est synchrone par nature, et il faut vraiment se faire chier pour obtenir un algo asynchrone. Ceci changera avec Python 3.4 qui accueillera tulip dans la stdlib, afin de se moderniser sur ce point. En attendant, si vous voulez faire de l’asynchrone en Python, vous pouvez voir du côté de gevent, monocle ou Tornado. L’alternative est d’utiliser des threads ou des processus séparés, ce qui ne demande rien à installer, mais est un peu verbeux, et est moins performant.

Souvenez-vous que l’I/O, c’est toute entrée et sortie du programme. Un clic sur un bouton, c’est une entrée, mettre à jour un élément du DOM dans le navigateur, c’est une sortie. La programmation asynchrone est donc importante pour la réactivité des programmes.

Si un algorithme peut répartir son travail en plusieurs bouts, c’est parallèle

Par exemple, vous avez 1000 images en haute définition à traiter : il faut les redimensionner, les mettre en noir et blanc et ajouter une ombre sur les bords. Là, la partie de votre programme qui prend le plus de temps, c’est le traitement des images, pas l’I/O, et donc c’est le CPU. Par exemple, en Python :

for image in obtenir_liste_images():
	# I/O
	data = lire_image(image) 
 
	# gros du travail
	redimensioner(data)
	mettre_en_noir_et_blanc(data)
	ajouter_ombre(data)
 
	# I/O
	ecrire_image(data, image)

Si vous avez plusieurs ordinateurs, une manière de paralléliser le travail est de mettre 500 images sur l’un, et 500 images sur l’autre, et de lancer le script sur chaque ordi.

Si vous avez plusieurs processeurs dans votre ordi (ce qui est le cas de tous les ordis modernes, et plus seulement les super-calculateurs comme il y a 10 ans), vous pouvez aussi paralléliser le travail sur une seule machine : chaque processeur va s’occuper d’une partie du taf.

Bien entendu, vous pouvez lancer le script 2 fois, mais cela ne marche que sur des travaux simples comme celui là. Et ça suppose que vous connaissez le nombre de CPU que vous voulez faire travailler à l’avance.

Une manière de faire plus propre est d’utiliser des threads ou des processus séparés. En Python, le thread ne servirait à rien, car on se heurterait au GIL, le fameux global interpréteur lock, qui fait qu’une VM n’utilise qu’un processeur, quoi qu’il arrive. Les threads ne sont donc utiles (en Python), que pour l’I/O. Par contre on peut utiliser plusieurs processus :

from multiprocessing import Process
 
def traiter_les_images(debut, fin):
 
 for image in obtenir_liste_images()[debut, fin]:
	# I/O
	data = lire_image(image) 
 
	# gros du travail
	redimensioner(data)
	mettre_en_noir_et_blanc(data)
	ajouter_ombre(data)
 
	# I/O
	ecrire_image(data, image)
 
# On crée deux processus, un pour traiter les 500 premières images,
# un pour traiter les images de 500 à 1000
p1 = Process(target=traiter_les_images, args=(0, 500))
p2 = Process(target=traiter_les_images, args=(500, 1000))
# On les démarre, ils se séparent alors du programme pour
# devenir indépendant
p1.start()
p2.start()
# on dit au programme d'attendre la fin des deux processus
# CE programme bloque ici, mais les deux processus, eux,
# ne bloquent pas.
p1.join()
p2.join()

Dans cet exemple, il y a TROIS processus : votre programme Python, et les deux processus qui vont traiter les photos, qui consistent ni plus ni moins en la fonction traiter_les_images() qui a maintenant un process pour elle toute seule.

La plupart des langages ont ce genre de mécanisme pour faire du travail en parallèle. Java utilise les threads par exemple. Javascript utilise les Web Workers.

Nous traitons des données de plus en plus massives (jeux vidéos, encoding divx, retouche d’images, montage de sons…), et maîtriser la parallélisation permet donc d’optimiser les ressources de nos machines modernes afin d’être toujours plus efficace.

Si il y a plusieurs entités indépendantes, c’est concurrent

Si vous avez un serveur et un client, c’est de la programmation concurrente. Si vous avez un module qui s’occupe des I/O utilisateurs, un qui s’occupe de la base de données et un qui surveille le comportement de l’OS, dans des processus séparés, et qui communiquent entre eux, c’est de la programmation concurrente.

La programmation concurrente suppose que chaque acteur de votre système est indépendant et possède son propre état. Idéalement, les acteurs sont capables de communiquer entre eux. Généralement, ils partagent une ressource à laquelle ils doivent accéder, par exemple un fichier de log. Et c’est là qu’il faut faire attention : certaines ressources ne sont pas faites pour êtres utilisées en même temps par plusieurs process. C’est pour ça qu’on parle d’accès concurrent comme d’un gros problème en informatique.

Un exemple de programmation concurrente en Python serait d’avoir un process qui regarde régulièrement si il y a des mails, et les sauvegarde. Si il reçoit un message suspect, il envoie le message à un autre process, un anti-virus, qui en plus de surveiller l’ordi, peut désinfecter le mail. Exemple :

from multiprocessing import Process, Queue
 
entree_traiteur_de_mail = Queue()
entree_anti_virus = Queue()
 
def traiter_les_mails():
 
	# Les processus qui tournent continuellement
	# en arrière plan sont juste boucle infinie
	while True:
		mail = obtenir_mail()
		# Si un mail est suspect, on l'envoie
		# au processus de l'anti-virus, 
		# et on attend qu'il nous le renvoie
		# tout propres.
		# Les deux processus sont indépendant,
		# ils fonctionnent l'un sans l'autre et
		# ne sont pas dans la même VM.
		if mail_est_suspect(mail):
			entree_anti_virus.put(mail)
			mail = entree_traiteur_de_mail.get()
		sauvegarder_mail(mail)
 
 
def anti_virus():
 
	while True:
		# L'anti-virus vérifie périodiquement 
		# s'il n'a pas un mail à nettoyer,
		# mais n'attend que 0.01 seconde, et si
		# rien ne se présente, continue son 
		# travail.
        try:
        	# Si il y a un mail à désinfecter,
        	# il le nettoie, et le renvoie
        	# au processus de traitement de mails.
            mail = entree_anti_virus.get(0.01)
            desinfecter_mail(mail)
            entree_traiteur_de_mail.put(mail)
        except TimeoutError:
            pass
        # L'anti-virus ne fait pas que desinfecter 
        # les mails, il a d'autres tâches à lui
        verifier_virus_sur_system()
 
 
# On lance les process. La plupart du temps, il n'y a 
# pas de mail suspect, et donc les deux processus
# n'en bloquent pas. En cas de mail suspect ils bloquent
# le temps d'échanger le mail entre eux.
process_anti_virus = Process(target=traiter_les_mails)
process_traitement_mail = Process(target=anti_virus)
process_anti_virus.start()
process_traitement_mail.start()
process_anti_virus.join()
process_traitement_mail.join()

La programmation concurrente est donc une question d’architecture : vous êtes en concurrence ou non si vous décidez de répartir votre code entre plusieurs acteurs indépendant ou non. Les acteurs peuvent avoir des tâches distinctes, et ne pas se bloquer, mais communiquer sur les tâches communes. L’avantage de la programmation concurrente, c’est sa robustesse : si un process plante, le reste de votre programme continue de fonctionner. C’est pour cette raison qu’Erlang, un langage connu pour créer des systèmes increvables, base toute sa philosophie là dessus : un programme Erlang est composé de milliers d’acteurs communiquant entre eux par messages.

Hey, mais, attends là !

Ton exemple de programmation parallèle, c’est aussi une exécution concurrente. Et puis si on fait pleins de processus, pour faire la même tâche d’I/O, ils ne se bloquent pas entre eux, donc c’est non bloquant sur l’I/O, c’est asynchrone !

Allez-vous me dire, fort intelligement. Car nous avons des lecteurs intelligents.

Hé oui, effectivement, ce sont des notions qui se chevauchent. Comme je vous l’ai dit, c’est une question de point de vue. Si on se place du point de vue de l’algo, on peut paralléliser le traitement, ou non. Et il y a plusieurs manières de paralléliser. Si on se place du point de vu de l’I/O, on peut bloquer ou non, et alors on est dans de l’asynchrone. Si on se place du point de vu des acteurs, on peut en avoir plusieurs indépendants ou non, alors on est en concurrence.

En fait, même plusieurs acteurs qui communiquent entre eux sont considérés comme étant chacun en train de faire de l’I/O, avec les autres…

Bref, ces 3 termes, c’est de la sémantiques. Au final, ce qui importe, c’est que vous compreniez les enjeux qu’il y a derrière pour écrire un programme qui fasse son boulot comme il faut, et finisse en temps et en heure.

flattr this!

]]>
http://sametmax.com/la-difference-entre-la-programmation-asynchrone-parallele-et-concurrente/feed/ 24
Envie de meurtre http://sametmax.com/envie-de-meurtre/ http://sametmax.com/envie-de-meurtre/#comments Fri, 19 Jul 2013 09:55:57 +0000 Sam http://sametmax.com/?p=6725 Oui, je tape beaucoup sur nodejs et la communauté javascript en général. Mais ils le méritent, bordel !

Je tombe sur un projet OpenSource qui est sorti en janvier dernier. Ils vendent leur popote “As A Service”, donc a priori, on pourrait se dire qu’ils voudraient qu’on les prennent au sérieux.

Ils fournissent un serveur pour se faire la main en local avant de taper dans leur API payante. Cool. Merci.

D’abord, il faut évidement passer par l’installation de node et tout le bordel. Bon, admettons. Même si Python est installé par défaut, il faut bien installer pip.

Mais ensuite, au lancement du serveur, rien. Il reste là, silencieusement. Pas de mode verbose, un mode debug qui ne fait absolument rien.

Mon code client se connecte, et puis nada. Silence radio sur leur lib client et serveur. Vide intersidéral.

La doc est succincte (3 pages) et rien qui ne parle du problème. Question sur la mailling list. Pas de réponse.

A ce stade j’en ai déjà ras le cul. Les libs node qui marchent pas et qui en plus veulent pas mettre un putain de message d’erreur, y en a déjà un paquet. C’est une épidémie dans cette communauté.

Mais je retrousse mes manches, et je vais voir dans le code source.

Et là, je tombe sur 400 lignes de ça :

    var client;
    if (!self._clients[key] || !(client = self._clients[key][id])) {
      if (req.params.retry) {
        res.send(401);
      } else {
        // Retry this request
        req.params.retry = true;
        setTimeout(handle, 25, req, res);
      }
      return;
    }

C’est un appel au génocide, un style de coding comme ça ! Sérieusement, déjà Javascript est un langage moche, avec un scoping de merde, du nesting en pagaille et tout un tas de pièges qui fait que le code est dur à lire et à comprendre.

Mais là, vous imaginez la concentration qu’il faut pour lire ces 10 putains de lignes ? Et les remettre dans le contexte du script complet (qui au passage est commenté comme un diabétique sucre son café).

C’est trop dur de ne pas pondre des trucs innommables comme !(client = self._clients[key][id]) ? Un ligne de plus a taper. Trop difficile de mettre des noms de variables complets ? id, mais id de quoi connard ? Je remonte tout le script pour m’en souvenir ? Et pourquoi tu assignes client = self._clients si c’est pour ne pas t’en servir ? Et puis un double if imbriqué suivi d’un appel récursif asynchrone, c’est tellement fun pour suivre le workflow !

400 lignes comme ça, après le dîner, ça fout la chiasse. Alors oui, c’est de l’open source, faut pas faire le difficile, etc.

Mais bordel, qu’on ne vienne pas me dire que c’est l’avenir. Les ex-script kiddies d’hier, codeurs PHP et VB, commencent à peine à écrire du code propre. Et là grosso modo on va se taper la vague des intégrateurs Web qui se mettent à coder parce que c’est du JS comme dans le navigateur ?

GGGGGGGGGGGGRRRRRRRRRRRRRRRRRRRRRRRR !

Je devrais faire une émission style “joueur du grenier”, mais pour le code pourri.

flattr this!

]]>
http://sametmax.com/envie-de-meurtre/feed/ 42
Créer un bookmarklet http://sametmax.com/creer-un-bookmarklet/ http://sametmax.com/creer-un-bookmarklet/#comments Fri, 31 May 2013 12:49:54 +0000 Sam http://sametmax.com/?p=3972 Vous avez un super outil JS que vous voulez partager avec le monde entier ? Faites un bookmarklet ! Il s’agit de compresser tout votre code sur une ligne de telle sorte qu’il puisse être mis en bookmark dans le browser et utilisé en un clic.

Problème : débugger un pâté de JS d’une ligne, c’est relou. Et pour les gros outils, il n’y a pas la place de toute coder. L’idée est donc de packer juste l’import de votre outil.

D’abord, mettre le script de votre outil quelque part (par exemple votre serveur Web). Pour l’article, je vais mettre le script sur yourjavascript.com.

Il affiche une des tag lines du blog avec clippy (on m’avait demandé une fois comment on faisait, ben voilà). Il ne marche que sur le blog et tout en haut à gauche d’une page non scrollée, mais OSEF, c’est un exemple.

Ensuite, on fait le code d’include, qui va juste créer une balise SCRIPT et ajoute votre outil une fois que la page est chargée :

// on frabrique le tag SCRIPT
var url = "http://yourjavascript.com/11356413275/test.js";
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
// on met la date dans l'url pour éviter le cache
script.src = url + '?' + (new Date().getTime());
// on ajoute le tag ce qui va charger votre outil
head.appendChild(script);

Puis il faut le mettre au format bookmarklet, c’est à dire :

  • Faire tenir le code sur une ligne.
  • L’enfermer dans une fonction appelée immédiatement.
  • Transformer tout ça en URL (urlencoder + ajouter le protocole “javascript:”)

Faites le une fois pour vous entraîner à comprendre ce qui se passe (avec un code plus simple, genre un alert()) :

javascript:(function(){alert("test")})()

Ensuite faites le avec votre vrai code, encore à la main. Une fois. Vous noterez qu’il faut un code super propre (pas de “;” manquant) pour que ça marche.

Quand vous en aurez marre de galérer, vous ferez comme moi, vous utiliserez un outil en ligne pour le faire automatiquement.

Le code résultant est celui-ci:

javascript:(function(){var%20url=%22http://yourjavascript.com/11356413275/test.js%22;var%20head=document.getElementsByTagName('head')[0];var%20script=document.createElement('script');script.type='text/javascript';script.src=url+'%3F'+(new%20Date().getTime());head.appendChild(script);})();

On peut le mettre directement à la place de l’URL HTTP dans un bookmark. Si on clique sur le bookmark, il va lancer ce code, qui va ajouter la balise SCRIPT avec mon autre code, qui va se lancer et faire apparaitre clippy pour notre plus grand plaisir.

Comme je suis une feignasse, clippy apparait en haut de la page, donc il vous faudra scroller tout en haut pour voir le résultat. J’avais pas envie de me taper les calculs pour faire une posionnement relatif au viewport.

Personnellement je m’en sers pour faire des petits outils comme lire des sites plein d’images en mettant toutes les images à la suite des autres en auto loading, pour charger des outils d’admin sur la page principale d’un site (genre le casting des videos pornos) ou juste debugger.

flattr this!

]]>
http://sametmax.com/creer-un-bookmarklet/feed/ 4
Faire cohabiter plusieurs versions de jQuery http://sametmax.com/faire-cohabiter-plusieurs-versions-de-jquery/ http://sametmax.com/faire-cohabiter-plusieurs-versions-de-jquery/#comments Sun, 12 May 2013 13:08:25 +0000 Sam http://sametmax.com/?p=3946 noConflict() (c'est beau l'open source quand même), elle permet également d'utiliser en même temps une version plus récente ou plus ancienne de son code, facilitant les migrations.]]> jQuery est très bien foutu, non content de permettre d’utiliser des libs concurrentes utilisant la même API avec noConflict() (c’est beau l’open source quand même), elle permet également d’utiliser en même temps une version plus récente ou plus ancienne de son code, facilitant les migrations.

Pour utiliser noConflict à bon escient, il faut simplement faire très attention à l’ordre d’inclusion de ses scripts…

D’abord, loader les deux versions de jQuery :

<script type="text/javascript" src="version1-de-jquery.js"></script>
<script type="text/javascript" src="plugin-qui-utilise-cette-version.js"></script>

Ensuite utilise noConflict() pour sauvegarder une référence à cette version :

<script type="text/javascript">
var version1dejQuery = $.noConflict(true);
</script>

Et on charge la deuxième fournée :

<script type="text/javascript" src="autre-version-de-jquery.js"></script>
<script type="text/javascript" src="plugin-qui-utilise-cette-autre-version.js"></script>

On sauvegarde aussi cette version dans une variable :

<script type="text/javascript">
var autreVersionDejQuery = $.noConflict(true);
</script>

Et du coup on peut utiliser les deux en parallèle. Au lieu d’appeler $, les appelle par leur p’tit nom :

autreVersionDejQuery('a').click(truc);

Et comme d’hab, vous pouvez utiliser la technique de la fonction anonyme immédiatement appelée pour créer un alias local et réutiliser $.

(function($) {
    $('img').hover(bidule);
})(version1dejQuery);

D’ailleurs, je pense que ça ne marche que si les plugins inclus utilisent cette technique, ce qui est normalement une convention dans le monde de jQuery.

flattr this!

]]>
http://sametmax.com/faire-cohabiter-plusieurs-versions-de-jquery/feed/ 3
Utilisez des variables globales avec JSLint http://sametmax.com/utilisez-des-variables-globales-avec-jslint/ http://sametmax.com/utilisez-des-variables-globales-avec-jslint/#comments Wed, 17 Apr 2013 16:13:05 +0000 Sam http://sametmax.com/?p=5508 super plugin Sublime Text).]]> Petite astuce si vous utilisez JSlint (par exemple via le super plugin Sublime Text).

Il va vous mettre en avant toutes les variables globales ou non déclarées précédement. C’est génial la plupart du temps, mais c’est un peu chiant pour des variables qui sont volontairement globales et mises à dispo par d’autres scripts comme les frameworks et libs.

Par exemple, il va vous déclarer que jQuery est une variable globale ou non déclarée. Pour éviter ça, mettez ce commentaire tout en haut du fichier :

/*global jQuery:true, $:true */

JSLint va le prendre en compte et ignorer ces variables.

On peut lister autant de variables qu’on le souhaite.

flattr this!

]]>
http://sametmax.com/utilisez-des-variables-globales-avec-jslint/feed/ 1
Qu’est-ce que Javascript et à quoi ça sert ? http://sametmax.com/quest-ce-que-javascript-et-a-quoi-ca-sert/ http://sametmax.com/quest-ce-que-javascript-et-a-quoi-ca-sert/#comments Sun, 07 Apr 2013 22:04:26 +0000 Sam http://sametmax.com/?p=5619 évident.]]> La question parait bête, mais je me la suis posé il y a des années. Et personne n’avait écrit un article pour expliquer ça. Personne. C’était tellement évident.

Cet article est pour toi, étudiant en info, commercial dans une boîte IT ou juste curieux qui trouve que l’article Wikipedia ne veut rien dire.

Ce que c’est

Javascript est un langage de programmation, c’est une forme de code qui permet, quand on sait l’écrire, de dicter à l’ordinateur quoi faire.

Le code Javascript ressemble à ça :

setTimeout(function(){
    if (truc == machin) {
        alert('Bidule !')
    }
}, 100)

C’est du texte. Juste du texte. Comme une autre langue.

On trouve la majorité du code Javascript dans des pages Web, même si vous ne le voyez pas s’afficher. En effet, c’est le seul langage qui permette de dicter à un navigateur Web (Internet Explorer, Firefox, Chrome…) ce qu’il doit faire sans rien installer. La grande majorité des navigateurs Web “parlent” le Javascript.

Ce qu’on peut faire avec

La plupart du code Javascript se trouve dans des pages Web, et sert donc à dire comme la page Web doit réagir. Cela marche ainsi :

  1. L’utilisateur clique sur un lien ou entre une adresse.
  2. Son navigateur charge la page Web. Il voit le texte, les couleurs, les images.
  3. Si la page Web contient du code Javascript, le navigateur lit le code Javascript et suit les instructions du code.

Généralement le code Javascript dans une page Web sert à :

  • Faire bouger, apparaitre ou disparaitre des éléments de la page (un titre, un menu, un paragraphe, une image…).
  • Mettre à jour des éléments de la page sans recharger la page (changer le texte, recalculer un nombre, etc).
  • Demander au serveur un nouveau bout de page et l’insérer dans la page en cours, sans la recharger.
  • Attendre que l’utilisateur face quelque chose (cliquer, taper au clavier, bouger la souris…) et réagir (faire une des opérations ci-dessus suite à cette action).

Le code Javascript sert donc à donner du dynamisme à la page. Sans lui, la page ressemble à une page de livre, un peu animée (grâce à un autre langage appelé le CSS), mais qui ne change pas beaucoup.

Par exemple, voici quelques fonctionnalités que l’on peut voir dans une page Web qui impliquent Javascript :

Cela ne veut pas dire qu’on ne pourrait pas avoir ces fonctionnalités avec autre chose que Javascript, mais dans notre cas, elles ont été produites avec Javascript.

Certains sites Web ne pourrait tout simplement pas fonctionner sans Javascript. C’est le cas de Facebook, Youtube ou Twitter qui utilisent le langage pour presque tout leur affichage. La page de recherche de Google, en revanche, peut fonctionner sans Javascript.

Voilà par exemple deux pages similaires. L’une avec Javascript (cliquez sur le texte pour voir l’effet) :

Et l’autre sans (cliquez, il ne se passe rien):

Quelques subtilités

Le code Javascript est lu et exécuté sur le navigateur Web, donc sur l’ordinateur de l’internaute.

C’est ce qu’on appelle du code “côté client”. Et il ne peut pas interagir directement avec le code “côté serveur” (celui qui a produit la page Web); et donc on ne peut pas communiquer directement entre le Javascript côté client et PHP côté serveur par exemple.

Comme l’internaute a le contrôle de sa machine, il peut choisir de désactiver le support de Javascript sur son navigateur. Dans ce cas, ce dernier ignorera le code Javascript et fera comme si il n’était pas là. Il verra la page, mais tous les éléments qui fonctionnent avec Javascript ne marcheront pas.

Les pages Web sont aussi parcourues par des navigateurs qui ne supportent pas Javascript, et par des robots qui ne sont pas des navigateurs Web. De nombreux moteur de recherche (comme Google, DuckDuckGo ou Bing) ont en effet de tels robots pour lister toutes les pages Web, et souvent leurs robots ignorent Javascript.

Javascript, quand il est dans le navigateur Web, a aussi cette particularité d’être un langage limité à ce que le navigateur lui laisse faire. C’est ce qui le rend assez sécurisé. Mais chaque navigateur a ses particularités et comprend parfois le Javascript différemment. De plus, l’exécution du code Javascript a pendant longtemps été quelque chose de lent.

Cela a rendu le langage impopulaire, il été évité pour sa difficulté d’utilisation, ses limites importantes et ses performances basses.

Javascript moderne

Aujourd’hui le langage Javascript est massivement utilisé sur tous les sites Web grand public. Des outils, tel que jQuery, on été développés pour faciliter son utilisation et les navigateurs sont devenus beaucoup plus efficaces dans son traitement.

La popularité grandissante dans les années 2000 d’un outil appelé AJAX, qui permet à Javascript de mettre à jour une page sans la recharger, a propulsé le langage sur le devant de la scène. L’utilisation d’AJAX rendant, si il elle est bien faite, la consultation du site plus rapide et fluide, on l’a rapidement retrouvé sur tous les sites les plus fréquentés.

La conséquence est qu’il est aujourd’hui impossible de se dire sérieusement développeur Web si l’on n’est pas capable d’écrire un minimum de code Javascript. Si vous voulez développer des sites Web, vous devez apprendre au moins les bases de Javascript. Si vous recrutez un développeur Web, assurez vous qu’il les connaisse.

Javascript permet aujourd’hui, avec le support d’autres technologies (Flash, HTML5, canvas, CSS3, WebGL…), de faire des choses très évoluées comme de la 3D, de la manipulation d’images, de sons et de videos. Bientôt, avec des innovations comme WebRTC, Javascript permettra de faire du Peer-to-peer et de la vision conférence à l’intérieur du navigateur Web :

Si vous voyez un jeu vidéo ou un film dans une page Web, un code Javascript est impliqué quelque part. Le langage a quitté depuis longtemps son statut de technologie “jouet” et est actuellement l’un de ceux qui possèdent le plus de potentiel d’évolution.

Des innovations importantes sont à attendre du côté de Javascript avec la maturation d’outils tels que MeteorJs qui permettent de coder, en Javascript, la génération des pages côté serveur ET le code client des navigateurs, tout en les faisant interagir automatiquement. Si cette phrase ne veut rien dire pour vous, dites vous seulement que c’est le seul langage à tenir cette promesse actuellement.

Javascript, en dehors d’un navigateur Web

Avec l’amélioration des performances de Javascript, le langage a été de plus en plus utilisé en dehors du navigateur Web. On le retrouve aujourd’hui un peu partout :

  • Sur les serveurs, l’exemple le plus célèbre étant NodeJS, un outil qui permet de générer les pages Web avant de les envoyer au navigateur.
  • Sur les interfaces des ordinateurs, il permet d’afficher des fenêtres et des boutons (Scripting QT, Gnome Shell).
  • Sur les téléphones, pour le moment sur FirefoxOS et PhoneGap, il permet d’écrire des applications.

Frameworks Javascript

Javascript est né en tant que technologie minimaliste. Le langage est puissant, mais ne fait pas grand chose de lui-même. Avec sa professionalisation sont apparus ce que l’on appelle des frameworks, c’est à dire des collections de code qui rendent le développement en Javascript plus rapide, plus facile et plus puissant.

Sur le navigateur, le plus connu et utilisé est sans aucun doute jQuery. Il est aussi celui que nous préférons utiliser chez Sam et Max, et il n’existe presque plus de raison de ne pas l’utiliser aujourd’hui. Par défaut, si rien d’évident ne s’oppose fortement à inclure jQuery, nous recommandons toujours de l’ajouter d’office à la première lettre du code Javascript tapée sur un projet Web.

Si vous ne savez pas utiliser jQuery mais devez programmer du Javascript pour une page Web, commencez par apprendre à utiliser jQuery. Coder en Javascript à la main de nos jours, c’est comme moudre sa farine soi-même, c’est instructif, mais pas très productif.

Il existe tout de même d’autres frameworks : Mootools, Prototype.js, YahooUI… Ceux qui existent encore actuellement sont ceux qui ont survécu à l’effet mode, et ce sont tous des outils aujourd’hui solides.

Ensuite il y a des bibliothèque spécialisées dans des problématiques telles que le routing, la manipulation de modèles, le templating, etc. Encore une fois, si ces termes ne vous parlent pas, c’est que vous n’avez pas encore besoin de ces bibliothèques. On retrouve dans le lot backbone.js, spine.js, mustache.js et bien d’autres.

Des frameworks hybrides couvrant de nombreuses problématiques à la fois sont arrivés ces dernières années, notamment knockout.js de Microsoft et AngularJS de Google qui sont encore très jeunes mais qui méritent d’être suivis de prêt.

Les mauvais côtés de Javascript

Malgré ma lucidité concernant l’importance grandissante de Javascript, j’aimerais partager avec vous des choses que les enthousiastes ne vous disent pas.

D’abord, c’est un langage extrêmement laid. Ça va vous sembler futile, mais ça se traduit par un temps d’apprentissage du langage plus élevé. Un temps de relecture BEAUCOUP plus élevé (et on lit le code des centaines de fois par jour). Un temps de debuggage plus long également. Et des erreurs plus nombreuses car le JS est très permissif. Bref, c’est un langage très improductif si on le compare à Ruby, Python ou même à PHP.

Pour donner une idée de la chose, voici un code Javascript que peut produire un débutant pour écrire la fameuse chanson 99 bottle of beers:

for (var i = 99 ; i > 0 ; i--) {
 
  j = i - 1;
 
  if (i != 1) { 
    icase = "bottles"; 
  } else { 
    icase = "bottle";
  }
 
  if (j != 1) {
    jcase = "bottles";
  } else {
    jcase = "bottle";
  }
 
  console.log(i + " " + icase + " of beer on the wall,");
  console.log(i + " " + icase + " of beer,");
  console.log("Take 1 down, pass it around,");
 
  if (j != 0) {
    console.log(j + " " + jcase + " of beer on the wall.");
  } else {
    console.log("No more bottles of beer on the wall!");
  }
 
  console.log()
}

Et voici l’équivalent (littéral) en Python :

for i in xrange(99, 0, -1):
 
    j = i - 1
 
    if i != 1:
        icase = "bottles"
    else:
        icase = "bottle"
 
    if j != 1:
        jcase = "bottles"
    else:
        jcase = "bottle"
 
    print "%s %s of beer on the wall" % (i, icase)
    print "%s %s of beer," % (i, icase)
    print "Take 1 down, pass it around,"
 
    if j:
        print "%s %s of beer on the wall." % (j, jcase)
    else:
        print "No more bottles of beer on the wall!"
 
    print

En plus d’être plus plus d’être plus long de 50 caractères, le code Javascript comporte deux bugs potentiels : les variables non déclarées fonctionnent, mais sont partagées avec tout le reste du code. Le console.log va lui aussi fonctionner sur un serveur et sur certains navigateurs, mais fera planter d’autres navigateurs qui n’ont pas accès à cet objet. On peut le remplacer par document.writeln mais alors le code ne marchera pas sur un serveur.

Les prérequis pour coder un simple bout de code Javascript sans erreur sont plus élevés, la vigilance demandée est plus grande.

Ensuite, il est difficile de trouver de bons développeurs Javascript. Les profils les plus courants sont des designers forcés à faire un peu de JS, ou des codeurs PHP qui de temps en temps insèrent du JS dans leurs pages. Les spécialistes Javascript sont rares, chers et pas faciles à embaucher.

D’ailleurs, pour former quelqu’un ou se former à Javascript, les ressources sont spartiates. Je ne parle pas des milliers de scripts tout fait prêts à être utilisés. Ça il y en a plein. Je parle de cours pour devenir un codeur sérieux, et sortir de la relation d’amateurisme que la majorité des développeurs Web ont avec Javascript.

Côté navigateur, le Javascript est mûr. En revanche, en dehors du navigateur, c’est une technologie qui est encore dans son enfance. Oui, je parierai cher qu’il va devenir très dominant dans les prochaines années. Mais en attendant, ceux qui s’y mettent maintenant vont payer les pots cassés : libs pas testées, mal documentées, incompatibles, maintenues avec des baguettes… quand elles existent.

Rien qu’installer de quoi coder en Javascript sur sa machine est une plaie : sous Linux et Mac, Ruby et Python sont installés par défaut. Javascript demande des étapes en plus. Sous Windows, c’est le parcours du combattant.

En clair : mettre trop de billes dans Javascript coûte cher, et c’est pour cela que je m’en tiens à développer avec sur des pages Web qui est son habitat naturel puisqu’il n’y a besoin de rien installer. C’est la raison de sa popularité initiale, et on est en train de faire exactement l’inverse en forçant son portage partout ailleurs.

Javascript n’est pas adapté à tout, et en ce moment, on veut le mettre à toutes les sauces. C’est à mon sens préjudiciable, très fortement influencé par un effet de mode, et met de côté d’excellents autres langages qui seraient beaucoup plus adaptés. Aussi, si vous devez faire de la programmation serveur ou d’interface graphique, je vous recommande plutôt Ruby ou Python, avec une préférence pour ce dernier somme toute biaisée par l’usage intensif qu’on en fait.

En prime ils vous rendront service pour le scripting, les traitements et l’analyse de données, le prototypage, et plein d’autres situations auxquelles vous n’aviez pas pensé. Ce sont des langages créés à l’origine pour être généralistes, pas adaptés durant les dernières années parce qu’ils ont le vent en poupe (Python est plus vieux que Java…).

Cela ne vous dispense pas de capitaliser sur des talents en Javascript en plus de ceux existant. Cela risque d’être plus qu’utile pour le futur.

flattr this!

]]>
http://sametmax.com/quest-ce-que-javascript-et-a-quoi-ca-sert/feed/ 32
Un code plus propre avec “use strict” en javascript http://sametmax.com/un-code-plus-propre-avec-use-strict-en-javascript/ http://sametmax.com/un-code-plus-propre-avec-use-strict-en-javascript/#comments Tue, 26 Mar 2013 10:30:20 +0000 Sam http://sametmax.com/?p=4002 un message de Dieu lui-même, et maintenant vous n'utilisez juste qu'un sous-ensemble du langage dans votre code. Mais cela demande de la vigilance. Une astuce peut vous aider à rester dans le droit chemin : "use strict".]]> Le JavaScript est très permissif. Mais vous avez reçu un message de Dieu lui-même, et maintenant vous n’utilisez juste qu’un sous-ensemble du langage dans votre code. Mais cela demande de la vigilance.

Une astuce peut vous aider à rester dans le droit chemin : "use strict".

Bien entendu, vous écrivez votre code dans une fonction appelée immédiatement. Ajoutez la chaîne de caractères "use strict"; juste en dessous de cette fonction pour que les interpréteurs des navigateurs modernes deviennent plus exigeants avec celui-ci :

(function(){
"use strict";
 
// votre code
 
})()

Pour les navigateurs qui ne connaissent pas cette instruction, c’est juste une string, et c’est ignoré. Pour ceux qui connaissent l’instruction, tout le bloc concerné (ici tout votre code, mais PAS le code en dehors de la fonction, donc pas de risque de planter le code des autres tels qu’un plugin jQuery) sera vérifié.

Parmi les vérifications :

  • Définir deux fois le même attribut dans un objet avec la notation littérale.
  • Utilisation de eval().
  • Utilisation de with.
  • Utilisation de arguments and caller.

Si ces situations sont rencontrées, une exception est levée. Un bon article avec des exemples en anglais ici.

Tous mes codes JS ont maintenant "use strict" par défaut, en plus du plugin SublimeLinter qui me sert de garde-fou supplémentaire.

flattr this!

]]>
http://sametmax.com/un-code-plus-propre-avec-use-strict-en-javascript/feed/ 13
Detecter la version d’IE en Javascript http://sametmax.com/detecter-la-version-die-en-javascript/ http://sametmax.com/detecter-la-version-die-en-javascript/#comments Tue, 19 Mar 2013 09:19:00 +0000 Sam http://sametmax.com/?p=5436 Sniffer le browser, c’est maaaaal. Mais comme toute technique diabolique, il existe quelques corner cases dans lesquels on peut la justifier. Par exemple, vous utilisez un plugin jQuery qui plante mystérieusement sous IE (toute ressemblance avec un événement réel n’est que purement accidentelle), et vous ne comprenez pas pourquoi. Vous ne pouvez donc pas détecter la fonctionnalité qu’il manque avec modernizr ou autre, seulement que ça plante sous IE. En attendant que votre ticket de bug soit pris en compte, il faut bien que votre site en prod marche. Donc, détection du browser, et mise en place de mesures de contournement.

Il y a d’autres cas utiles :

  • Faire des statistiques sur les versions d’IE sur votre site sans taper dans votre code backend.
  • Charger dynamiquement des feuilles de styles et scripts pour IE (en opposition aux commentaires conditionnels qui les chargent avec la page).
  • Afficher dynamiquement plus de pub aux utilisateurs d’IE car vous savez qu’ils sont moins tech saavy que les autres et cliquent plus facilement dessus.

Bref, la méthode la plus propre (pour un truc sale :-)) est d’utiliser des commentaires conditionnels pour mettre une classe au tag .

<!doctype html>
<!--[if lt IE 7 ]> <html class="ie6"> <![endif]-->
<!--[if IE 7 ]>    <html class="ie7"> <![endif]-->
<!--[if IE 8 ]>    <html class="ie8"> <![endif]-->
<!--[if IE 9 ]>    <html class="ie9"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html class=""> <!--<![endif]-->
<head>

Ainsi votre page va commencer avec :

<html>

si ce n’est pas IE ou si c’est IE10 (qui est presqu’un navigateur décent).

<html class="ie6">

si c’est IE6 ou moins.

<html class="ie7 ou ie8 ou ie9">

respectivement pour ie7, ie8 et ie 8

Et derrière, pour cibler une version d’IE dans le code JS, jQuery fait des merveilles avec la fonction is():

var browserDeMerde = $('html').is('.ie6, .ie7, .ie8, .ie9');

Rappelez-vous qu’avec de grands pouvoirs viennent de grandes responsabilités, alors n’abusez pas de cette techique, c’est aussi chiant que les mecs qui font des sites Webkit only.

flattr this!

]]>
http://sametmax.com/detecter-la-version-die-en-javascript/feed/ 10
Demander confirmation à la fermeture d’un onglet en javascript http://sametmax.com/demander-confirmation-a-la-fermeture-dun-onglet-en-javascript/ http://sametmax.com/demander-confirmation-a-la-fermeture-dun-onglet-en-javascript/#comments Mon, 18 Mar 2013 08:28:40 +0000 Sam http://sametmax.com/?p=5405 Votre utilisateur est en train d’écrire un commentaire, upload un ficher ou fait un truc quelconque qui n’est pas terminé. Puis il clique sur “fermer l’onglet”.

Dans certains cas, vous voulez lui demander confirmation pour éviter qu’il perde tout son travail.

var confirmOnLeave = function(msg) {
 
    window.onbeforeunload = function (e) {
        e = e || window.event;
        msg = msg || '';
 
        // For IE and Firefox
        if (e) {e.returnValue = msg;}
 
        // For Chrome and Safari
        return msg;
    };
 
};
 
// message de confirmation générique du navigateur
confirmOnLeave();
 
// message de confirmation personnalisé
confirmOnLeave('Vous allez perdre votre travail, êtes vous sûr(e) de vouloir quitter la page ?');

Quand vous appelez la fonction confirmOnLeave(), le callback de l’événement ‘onbeforeunload’ va être remplacé par une nouvelle fonction. Vous pouvez appeler cette fonction n’importe quand, l’événement ne se déclenche que quand l’utilisateur quitte la page de toute façon.

Cette fonction ne fait que retourner un message et assigner le message à l’attribut returnValue.

La navigateur va réagir ainsi:

  • À la fermeture de la page, il va déclencher l’événement ‘onbeforeunload’.
  • Au déclenchement de l’événement, il va appeler le callback.
  • Le navigateur va ensuite regarder la valeur de returnValue. Si elle n’est pas undefined, il va demander confirmation à l’utilisateur avec une pop up.
  • Le contenu de la pop up dépend de returnValue. Si c’est une chaîne vide, c’est un message générique, sinon, c’est le contenu de la chaîne qui est affiché.

Dans la plupart des cas, cette fonction suffit. Mais si vous utilisez d’autres codes JS qui ont besoin aussi de réagir à ‘onbeforeunload’ (comme par exemple pour faire un autosave), dans ce cas il vaut mieux utiliser utiliser une gestion avancée d’attachement de callback a des événements (pour qu’ils ne s’écrasent pas les uns les autres). Vous pouvez coder ça à la main, ou juste utiliser celui de jQuery qui fait ça très bien:

$(window).bind('beforeunload', function(){
    ...
});

flattr this!

]]>
http://sametmax.com/demander-confirmation-a-la-fermeture-dun-onglet-en-javascript/feed/ 18