« Je lance une fonction, et elle retourne undefined ! » — une douleur familière ?
\nImaginez : vous écrivez du code en JavaScript, vous faites une requête à une API, vous attendez les données… et en retour vous obtenez undefined. Ou pire — Promise {<pending>}. Vous baissez les bras, vous avez envie de fermer le navigateur et de devenir moine.
Ne vous précipitez pas. Ce problème tourmente même les développeurs expérimentés. Ce n'est pas pour rien que la question « How do I return the response from an asynchronous call? » sur Stack Overflow a rassemblé des milliers de vues et des centaines de réponses. Aujourd'hui, nous allons la résoudre une fois pour toutes.
\nDans cet article, vous apprendrez :
\n- \n
- Pourquoi
returnne fonctionne pas comme vous en avez l'habitude \n - Comment apprivoiser l'asynchrone à l'aide de callbacks, de promesses et de
async/await\n - Quelle approche choisir pour votre projet \n
C'est parti !
\n\nPourquoi return ne retourne-t-il pas les données ?
\nAllons droit au but. Voici un exemple classique :
\n\nfunction getUserData(userId) {\n fetch(`https://api.example.com/users/${userId}`)\n .then(response => response.json())\n .then(data => {\n return data; // Tout semble correct ?\n });\n}\n\nconst user = getUserData(42);\nconsole.log(user); // undefined\n\nFamiliar ? Vous avez écrit return data à l'intérieur de .then(), mais à l'extérieur vous obtenez undefined. Pourquoi ?
JavaScript est monothread et non bloquant. Lorsque vous appelez fetch, il n'attend pas la réponse du serveur. Au lieu de cela, il « met de côté » l'exécution du callback et passe immédiatement à la ligne suivante. Et la ligne suivante est console.log(user), où user n'est pas encore défini.
Votre fonction getUserData se termine avant que la réponse du serveur n'arrive. Elle ne retourne rien (elle retourne undefined), et les données « flottent » dans les airs.
Comment corriger cela ? Il existe trois méthodes principales.
\n\nMéthode 1 : Les callbacks — le grand-père de l'asynchrone
\nAvant l'apparition des promesses, toutes les opérations asynchrones se faisaient via des callbacks. Vous transmettez une fonction qui sera appelée lorsque les données arriveront.
\n\nfunction getUserData(userId, callback) {\n fetch(`https://api.example.com/users/${userId}`)\n .then(response => response.json())\n .then(data => {\n callback(data); // On appelle le callback avec les données\n })\n .catch(error => {\n callback(null, error); // Ou on transmet l'erreur\n });\n}\n\n// Utilisation :\ngetUserData(42, (data, error) => {\n if (error) {\n console.error('Erreur :', error);\n return;\n }\n console.log('Données utilisateur :', data);\n});\n\nAvantages : simplicité, fonctionne partout.
\nInconvénients : si vous imbriquez plusieurs callbacks les uns dans les autres, vous obtenez « l'enfer des callbacks » (callback hell). Le code devient illisible.
\n\ngetUserData(42, (data1) => {\n getPosts(data1.id, (data2) => {\n getComments(data2[0].id, (data3) => {\n // ... et ainsi de suite\n });\n });\n});\n\nLe JavaScript moderne s'en est éloigné. Mais il est nécessaire de comprendre les callbacks — ils sont à la base de toutes les autres approches.
\n\nMéthode 2 : Les promesses — les sauveuses de la lisibilité
\nUne promesse est un objet qui représente une « promesse » de retourner une valeur dans le futur. Elle a trois états : pending (en attente), fulfilled (résolue) et rejected (rejetée).
Revenons à notre exemple. Au lieu d'un callback, nous retournons la promesse elle-même :
\n\nfunction getUserData(userId) {\n return fetch(`https://api.example.com/users/${userId}`)\n .then(response => {\n if (!response.ok) {\n throw new Error('Le réseau n'est pas en ordre');\n }\n return response.json();\n });\n}\n\n// Utilisation :\nconst userPromise = getUserData(42);\n\nuserPromise\n .then(data => {\n console.log('Données utilisateur :', data);\n })\n