├── .gitignore ├── CONTRIBUTORS.md ├── README.md ├── async_you.js ├── exercises ├── each │ ├── exercise.js │ ├── problem.fr.md │ ├── problem.ja.md │ ├── problem.md │ ├── problem.ru.md │ ├── problem.uk.md │ └── solution │ │ └── solution.js ├── map │ ├── exercise.js │ ├── problem.fr.md │ ├── problem.ja.md │ ├── problem.md │ ├── problem.ru.md │ ├── problem.uk.md │ └── solution │ │ └── solution.js ├── menu.json ├── reduce │ ├── exercise.js │ ├── problem.fr.md │ ├── problem.ja.md │ ├── problem.md │ ├── problem.ru.md │ ├── problem.uk.md │ └── solution │ │ └── solution.js ├── series_object │ ├── exercise.js │ ├── problem.fr.md │ ├── problem.ja.md │ ├── problem.md │ ├── problem.ru.md │ ├── problem.uk.md │ └── solution │ │ └── solution.js ├── times │ ├── exercise.js │ ├── problem.fr.md │ ├── problem.ja.md │ ├── problem.md │ ├── problem.ru.md │ ├── problem.uk.md │ └── solution │ │ └── solution.js ├── waterfall │ ├── exercise.js │ ├── problem.fr.md │ ├── problem.ja.md │ ├── problem.md │ ├── problem.ru.md │ ├── problem.uk.md │ ├── solution │ │ └── solution.js │ └── url.txt └── whilst │ ├── exercise.js │ ├── problem.fr.md │ ├── problem.ja.md │ ├── problem.md │ ├── problem.ru.md │ ├── problem.uk.md │ └── solution │ └── solution.js ├── i18n ├── en.json ├── fr.json ├── help │ ├── en.txt │ ├── fr.txt │ ├── ja.txt │ ├── ru.txt │ └── uk.txt ├── ja.json ├── ru.json └── uk.json ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | project : async-you 4 | repo age : 8 months 5 | active : 29 days 6 | commits : 62 7 | files : 28 8 | authors : 9 | 48 Bulkan Evcimen 77.4% 10 | 8 jkbits1 12.9% 11 | 2 David McMullin 3.2% 12 | 1 Zorigt Bazarragchaa 1.6% 13 | 1 Nick Justice 1.6% 14 | 1 Nathan Marley 1.6% 15 | 1 Rodrigo Medeiros 1.6% 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-you 2 | 3 | **I see you** 4 | 5 | [![NPM](https://nodei.co/npm/async-you.png?downloads=true&stars=true)](https://nodei.co/npm/async-you/) [![NPM](https://nodei.co/npm-dl/async-you.png?months=3)](https://nodei.co/npm/async-you/) 6 | 7 | ## Introduction 8 | 9 | Learn to use the popular package [async](https://github.com/caolan/async) via this interactive workshop. 10 | 11 | Hopefully by the end this workshop you will understand the main functions that _async_ provides. 12 | 13 | ## Installation 14 | 15 | 1. Install [Node.js](http://nodejs.org/) 16 | 2. Run `npm install async` 17 | 3. Run `npm install async-you -g` , use `sudo` if you have permissions issues. 18 | 4. Run `async-you` to start the program! 19 | 20 | ## Usage 21 | 22 | #### 1. Selecting a problem to work on 23 | 24 | Once the workshop is installed, run `async-you` to print a menu 25 | where you can select a problem to work on. 26 | 27 | ``` 28 | $ async-you 29 | ``` 30 | 31 | Problems are listed in rough order of difficulty. You are advised to complete them in order, as later problems 32 | will build on skills developed by solving previous problems. 33 | 34 | #### 2. Writing your solution 35 | 36 | Once you have selected a problem, the workshop will remember which problem you are working on. 37 | Using your preferred editor, simply create a file to write your solution in. 38 | 39 | #### 3. Testing your solution 40 | 41 | Use the workshop's `run` command to point the workshop at your solution file. Your solution will be loaded 42 | and passed the problem input. This usually won't perform any validation, it will only show the program output. 43 | 44 | ``` 45 | $ async-you run mysolution.js 46 | ``` 47 | 48 | #### 4. Verifying your solution 49 | 50 | Your solution will be verified against the output of the 'official' solution. 51 | If all of the output matches, then you have successfully solved the problem! 52 | 53 | ``` 54 | $ async-you verify mysolution.js 55 | ``` 56 | 57 | ## Stuck? 58 | 59 | Feedback and criticism is welcome, please log your troubles in [issues](https://github.com/bulkan/async-you). 60 | 61 | ## Resources 62 | 63 | ## Thanks rvagg 64 | 65 | This tutorial was built using rvagg's [workshopper](https://github.com/rvagg/workshopper) framework. 66 | 67 | ## Licence 68 | MIT 69 | -------------------------------------------------------------------------------- /async_you.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const workshopper = require('workshopper-adventure'); 4 | const path = require('path'); 5 | 6 | const workshop = workshopper({ 7 | name: "async-you", 8 | appDir: __dirname, 9 | languages: ['en', 'fr', 'ru', 'uk'], 10 | helpFile: path.join(__dirname, "./i18n/help/{lang}.txt"), 11 | header: require('workshopper-adventure/default/header'), 12 | footer: require('workshopper-adventure/default/footer') 13 | }); 14 | 15 | workshop.addAll(require('./exercises/menu.json')); 16 | workshop.execute(process.argv.slice(2)); -------------------------------------------------------------------------------- /exercises/each/exercise.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , exercise = require('workshopper-exercise')() 3 | , filecheck = require('workshopper-exercise/filecheck') 4 | , execute = require('workshopper-exercise/execute') 5 | , comparestdout = require('workshopper-exercise/comparestdout') 6 | 7 | 8 | // checks that the submission file actually exists 9 | exercise = filecheck(exercise) 10 | 11 | // execute the solution and submission in parallel with spawn() 12 | exercise = execute(exercise) 13 | 14 | // compare stdout of solution and submission 15 | exercise = comparestdout(exercise) 16 | 17 | 18 | // set up the data file to be passed to the submission 19 | exercise.addSetup(function (mode, callback) { 20 | // mode == 'run' || 'verify' 21 | 22 | this.server = http.createServer(function (req, res) { 23 | res.end('meerkat'); 24 | }) 25 | 26 | this.server.listen(9345, callback) 27 | 28 | var urls = ['http://localhost:9345', 'http://localhost:4242'] 29 | this.submissionArgs = urls 30 | this.solutionArgs = urls 31 | }) 32 | 33 | 34 | // cleanup for both run and verify 35 | exercise.addCleanup(function (mode, passed, callback) { 36 | // mode == 'run' || 'verify' 37 | 38 | if (!this.server) 39 | return process.nextTick(callback) 40 | 41 | this.server.close(callback) 42 | }) 43 | 44 | 45 | module.exports = exercise 46 | -------------------------------------------------------------------------------- /exercises/each/problem.fr.md: -------------------------------------------------------------------------------- 1 | À l’occasion, vous aurez besoin d’appeler la même fonction plusieurs fois, 2 | mais avec des arguments distincts, **sans vous soucier des données retournées** 3 | mais en vérifiant que les appels n’ont pas généré d’erreur (parfois, même 4 | cette vérification vous importera peu). 5 | 6 | Dans ce genre de cas, `async.each` est parfait. 7 | 8 | Par exemple, le code suivant fait trois appels HTTP basés sur les valeurs 9 | d’un tableau : 10 | 11 | ```js 12 | var http = require('http') 13 | , async = require('async'); 14 | 15 | async.each(['cat', 'meerkat', 'penguin'], function(item, done){ 16 | var opts = { 17 | hostname: 'http://httpbin.org', 18 | path: '/post', 19 | method: 'POST' 20 | }; 21 | var req = http.request(opts, function(res){ 22 | res.on('data', function(chunk){ 23 | }); 24 | res.on('end', function(){ 25 | return done(); 26 | }); 27 | }); 28 | req.write(item); 29 | req.end(); 30 | }, 31 | function(err){ 32 | if (err) console.log(err); 33 | }); 34 | ``` 35 | 36 | ## Défi 37 | 38 | Écrivez un programme qui recevra deux URLs comme premier et second arguments 39 | de sa ligne de commande. 40 | 41 | Utilisez `http.get()` pour créer deux requêtes GET sur ces URLs et faites 42 | un `console.log` en cas d’erreur. 43 | -------------------------------------------------------------------------------- /exercises/each/problem.ja.md: -------------------------------------------------------------------------------- 1 | 時折、同じ関数を複数回呼び出したいときがあるでしょう。しかし異なる入力で、**戻り値を考慮せずに** 2 | しかしエラーはチェックしたい。 3 | 4 | このようなときに`async.each`は便利です。 5 | 6 | 例えば、次のように配列を使って3回コールする例を見てみましょう。 7 | 8 | ```js 9 | var http = require('http') 10 | , async = require('async'); 11 | async.each(['cat', 'meerkat', 'penguin'], function(item, done){ 12 | var opts = { 13 | hostname: 'http://httpbin.org', 14 | path: '/post', 15 | method: 'POST' 16 | }; 17 | var req = http.request(opts, function(res){ 18 | res.on('data', function(chunk){ 19 | }); 20 | res.on('end', function(){ 21 | return done(); 22 | }); 23 | }); 24 | req.write(item); 25 | req.end(); 26 | }, 27 | function(err){ 28 | if (err) console.log(err); 29 | }); 30 | ``` 31 | 32 | ## チャレンジ 33 | 34 | コマンドラインからfirstとsecondの2つのURLを受け取るプログラムを作成してください。 35 | その時、`http.get`を使い、URLを受け取るGET requestを作り、エラーがあれば`console.log`に出力してください。 36 | -------------------------------------------------------------------------------- /exercises/each/problem.md: -------------------------------------------------------------------------------- 1 | Occasionally you will want to call the same function multiple times, but with 2 | different inputs, **without caring about the return data** but to check if any call 3 | throws an error (sometimes not even that). 4 | 5 | This is where `async.each` is useful. 6 | 7 | For example, the following will make three calls using the values in the array: 8 | 9 | ```js 10 | var http = require('http') 11 | , async = require('async'); 12 | async.each(['cat', 'meerkat', 'penguin'], function(item, done){ 13 | var opts = { 14 | hostname: 'http://httpbin.org', 15 | path: '/post', 16 | method: 'POST' 17 | }; 18 | var req = http.request(opts, function(res){ 19 | res.on('data', function(chunk){ 20 | }); 21 | res.on('end', function(){ 22 | return done(); 23 | }); 24 | }); 25 | req.write(item); 26 | req.end(); 27 | }, 28 | function(err){ 29 | if (err) console.log(err); 30 | }); 31 | ``` 32 | 33 | ## Challenge 34 | 35 | Create a program that will receive two URLs as the first and second command-line 36 | arguments. 37 | 38 | Then using `http.get`, create two GET requests, one to each URL, and `console.log` 39 | any errors. 40 | -------------------------------------------------------------------------------- /exercises/each/problem.ru.md: -------------------------------------------------------------------------------- 1 | Время от времени вы бы хотели вызывать одну и ту же функцию несколько раз, но 2 | с разными параметрами, **не беспокоясь о том, какие данные возвращаются**, но 3 | проверяя, если вызовы выдают ошибки. 4 | 5 | В данном случае будет полезен `async.each`. 6 | 7 | Например, следующий код будет делать три вызова и использовать значения в массиве: 8 | 9 | ```js 10 | var http = require('http') 11 | , async = require('async'); 12 | async.each(['cat', 'meerkat', 'penguin'], function(item, done){ 13 | var opts = { 14 | hostname: 'http://httpbin.org', 15 | path: '/post', 16 | method: 'POST' 17 | }; 18 | var req = http.request(opts, function(res){ 19 | res.on('data', function(chunk){ 20 | }); 21 | res.on('end', function(){ 22 | return done(); 23 | }); 24 | }); 25 | req.write(item); 26 | req.end(); 27 | }, 28 | function(err){ 29 | if (err) console.log(err); 30 | }); 31 | ``` 32 | 33 | ## Задача 34 | 35 | Создать программу, которая получает два URL, как первый и второй аргументы командной строки. 36 | 37 | Затем, используя `http.get`, чтобы сделать два GET запроса, по одному на каждый URL, 38 | и `console.log` на любые ошибки. 39 | -------------------------------------------------------------------------------- /exercises/each/problem.uk.md: -------------------------------------------------------------------------------- 1 | Час від часу ви б хотіли викликати одну і ту ж саму функцію декілька разів, але 2 | з різними параметрами, **не турбуючись по те, які дані повертаються**, але 3 | перевіряючи, чи призводять виклики до помилок. 4 | 5 | В даному випадку буде корисним `async.each`. 6 | 7 | Наприклад, наступний код здійснює три виклики та використовує значення із масиву: 8 | 9 | ```js 10 | var http = require('http') 11 | , async = require('async'); 12 | async.each(['cat', 'meerkat', 'penguin'], function(item, done){ 13 | var opts = { 14 | hostname: 'http://httpbin.org', 15 | path: '/post', 16 | method: 'POST' 17 | }; 18 | var req = http.request(opts, function(res){ 19 | res.on('data', function(chunk){ 20 | }); 21 | res.on('end', function(){ 22 | return done(); 23 | }); 24 | }); 25 | req.write(item); 26 | req.end(); 27 | }, 28 | function(err){ 29 | if (err) console.log(err); 30 | }); 31 | ``` 32 | 33 | ## Завдання 34 | 35 | Написати програму, яка отримує два URL, як перший та другий аргументи командної строки. 36 | 37 | Потім, використовуючи `http.get`, зробити два GET запити, по одному на кожен URL, 38 | і `console.log` на будь-які помилки. 39 | -------------------------------------------------------------------------------- /exercises/each/solution/solution.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const { get } = require("http"); 3 | const async = require("async"); 4 | 5 | async.each( 6 | process.argv.slice(2), 7 | function (url, done) { 8 | get(url, (res) => { 9 | res 10 | .on("data", (chunk) => {}) 11 | .on("end", () => done(null)) 12 | .on("error", done); 13 | }).on("error", done); 14 | }, 15 | function (err) { 16 | if (err) console.error(err); 17 | } 18 | ); 19 | -------------------------------------------------------------------------------- /exercises/map/exercise.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , url = require('url') 3 | , exercise = require('workshopper-exercise')() 4 | , filecheck = require('workshopper-exercise/filecheck') 5 | , execute = require('workshopper-exercise/execute') 6 | , comparestdout = require('workshopper-exercise/comparestdout') 7 | 8 | 9 | // checks that the submission file actually exists 10 | exercise = filecheck(exercise) 11 | 12 | // execute the solution and submission in parallel with spawn() 13 | exercise = execute(exercise) 14 | 15 | // compare stdout of solution and submission 16 | exercise = comparestdout(exercise) 17 | 18 | 19 | // set up the data file to be passed to the submission 20 | exercise.addSetup(function (mode, callback) { 21 | // mode == 'run' || 'verify' 22 | 23 | this.server = http.createServer(function (req, res) { 24 | if(url.parse(req.url).pathname === '/one') 25 | return setTimeout(function() { 26 | res.end('one is smaller than 2'); 27 | }, 100); 28 | res.end('two greater than one'); 29 | }) 30 | 31 | this.server.listen(3131, callback) 32 | 33 | var urls = ['http://localhost:3131/one', 'http://localhost:3131/two'] 34 | this.submissionArgs = urls 35 | this.solutionArgs = urls 36 | }) 37 | 38 | 39 | // cleanup for both run and verify 40 | exercise.addCleanup(function (mode, passed, callback) { 41 | // mode == 'run' || 'verify' 42 | 43 | if (!this.server) 44 | return process.nextTick(callback) 45 | 46 | this.server.close(callback) 47 | }) 48 | 49 | 50 | module.exports = exercise 51 | -------------------------------------------------------------------------------- /exercises/map/problem.fr.md: -------------------------------------------------------------------------------- 1 | Avec `async.each()`, les résultats des fonctions asynchrones sont **perdus**. 2 | 3 | C’est pourquoi on utilise souvent `async.map()`. Elle fait la même chose 4 | que `async.each()`, mais lorsqu’elle itère de façon asynchrone sur le tableau, 5 | elle **récupère les résultats** et les passe à terme à la fonction de rappel 6 | qui leur est dédiée. 7 | 8 | Ces résultats sont présentés dans un tableau qui **respecte l’ordre** du tableau 9 | de valeurs d’origine. 10 | 11 | Par exemple, le code de l’exercice **each** peut être écrit comme suit : 12 | 13 | ```js 14 | var http = require('http') 15 | , async = require('async'); 16 | 17 | async.map(['cat', 'meerkat', 'penguin'], function(item, done){ 18 | var opts = { 19 | hostname: 'http://httpbin.org', 20 | path: '/post', 21 | method: 'POST' 22 | }; 23 | var body = ''; 24 | var req = http.request(opts, function(res){ 25 | res.on('data', function(chunk){ 26 | body += chunk.toString(); 27 | }); 28 | res.on('end', function(){ 29 | return done(null, body); 30 | }); 31 | }); 32 | req.write(item); 33 | req.end(); 34 | }, 35 | function(err, results){ 36 | if (err) return console.log(err); 37 | // `results`est un tableau des corps de réponse, dans le bon ordre 38 | }); 39 | ``` 40 | 41 | ## Défi 42 | 43 | Écrivez un programme avec deux URLs en arguments de ligne de commande. 44 | 45 | Utilisez `http.get()` pour créer deux requêtes GET sur ces URLs. 46 | 47 | Utilisez `async.map()` et faites à terme un `console.log()` du tableau 48 | des résultats. 49 | -------------------------------------------------------------------------------- /exercises/map/problem.ja.md: -------------------------------------------------------------------------------- 1 | `async.each`を使うと、非同期関数の結果は、 **失われます**。 2 | 3 | そこで`async.map`を使いましょう。 4 | これは配列上の非同期イテレータ関数を呼び出して`async.each`と同じことを 5 | しますが、**結果を収集し**コールバックへ渡します。 6 | 7 | 結果は、元の配列のように**同じ順序**である配列です。 8 | 9 | 例えば、それぞれの問題の例は次のように記述することができます: 10 | 11 | ```js 12 | var http = require('http') 13 | , async = require('async'); 14 | async.map(['cat', 'meerkat', 'penguin'], function(item, done){ 15 | var opts = { 16 | hostname: 'http://httpbin.org', 17 | path: '/post', 18 | method: 'POST' 19 | }; 20 | var body = ''; 21 | var req = http.request(opts, function(res){ 22 | res.on('data', function(chunk){ 23 | body += chunk.toString(); 24 | }); 25 | res.on('end', function(){ 26 | return done(null, body); 27 | }); 28 | }); 29 | req.write(item); 30 | req.end(); 31 | }, 32 | function(err, results){ 33 | if (err) return console.log(err); 34 | // results is an array of the response bodies in the same order 35 | }); 36 | ``` 37 | 38 | ## チャレンジ 39 | 40 | コマンドラインから2つのURLを受け取るプログラムを作成してください。 41 | `http.get`を使い、URLを受け取るGET requestを2つ作成してください。 42 | `async.map`を使い、`console.log`に結果配列を出力してください。 43 | -------------------------------------------------------------------------------- /exercises/map/problem.md: -------------------------------------------------------------------------------- 1 | With `async.each`, the results of the asynchronous function are **lost**. 2 | 3 | This is where `async.map` comes in. It does the same thing as `async.each`, 4 | by calling an asynchronous iterator function on an array, but **collects 5 | the results** of the asynchronous iterator function and passes them to the 6 | results callback. 7 | 8 | The results are in an array that is in the **same order** as the original array. 9 | 10 | For example, the example in the EACH problem can be written as: 11 | 12 | ```js 13 | var http = require('http') 14 | , async = require('async'); 15 | async.map(['cat', 'meerkat', 'penguin'], function(item, done){ 16 | var opts = { 17 | hostname: 'http://httpbin.org', 18 | path: '/post', 19 | method: 'POST' 20 | }; 21 | var body = ''; 22 | var req = http.request(opts, function(res){ 23 | res.on('data', function(chunk){ 24 | body += chunk.toString(); 25 | }); 26 | res.on('end', function(){ 27 | return done(null, body); 28 | }); 29 | }); 30 | req.write(item); 31 | req.end(); 32 | }, 33 | function(err, results){ 34 | if (err) return console.log(err); 35 | // results is an array of the response bodies in the same order 36 | }); 37 | ``` 38 | 39 | ## Challenge 40 | 41 | Write a program that will receive two command-line arguments to two URLs. 42 | 43 | Using `http.get` create two GET requests to these URLs. 44 | 45 | You will need to use `async.map`, then `console.log` the results array. 46 | -------------------------------------------------------------------------------- /exercises/map/problem.ru.md: -------------------------------------------------------------------------------- 1 | С `async.each`, результаты асинхронной функции будут **потеряны**. 2 | 3 | На помощь приходит `async.map`. Он делает то же самое, что и `async.each`, 4 | путем вызова асинхронной функции итератора на массив, но **собирает результаты** 5 | асинхронной функции итератора и передает результат в функцию обратного вызова. 6 | 7 | Результаты представлены в виде массива, которые находятся **в том же порядке**, 8 | что и в исходном массиве. 9 | 10 | Пример в EACH-задаче можно записать в виде: 11 | 12 | ```js 13 | var http = require('http') 14 | , async = require('async'); 15 | async.map(['cat', 'meerkat', 'penguin'], function(item, done){ 16 | var opts = { 17 | hostname: 'http://httpbin.org', 18 | path: '/post', 19 | method: 'POST' 20 | }; 21 | var body = ''; 22 | var req = http.request(opts, function(res){ 23 | res.on('data', function(chunk){ 24 | body += chunk.toString(); 25 | }); 26 | res.on('end', function(){ 27 | return done(null, body); 28 | }); 29 | }); 30 | req.write(item); 31 | req.end(); 32 | }, 33 | function(err, results){ 34 | if (err) return console.log(err); 35 | // Результаты - это массив тел ответов в том же порядке. 36 | }); 37 | ``` 38 | 39 | ## Задача 40 | 41 | Напишите программу, которая будет принимать два аргумента командной строки для двух URL. 42 | 43 | Используйте `http.get`, чтобы создать два GET-запроса по этим URL. 44 | 45 | Вам нужно будет использовать `async.map`, затем используйте `console.log` для результата. 46 | -------------------------------------------------------------------------------- /exercises/map/problem.uk.md: -------------------------------------------------------------------------------- 1 | З `async.each` результати асинхронної функції будуть **втрачені**. 2 | 3 | На допомогу приходить `async.map`. Він робить те ж саме, що і `async.each`, 4 | шляхом виклику асинхронної функції ітератора на масив, але **збирає результат** 5 | асинхронної функції ітератора і передає його в функцію зворотнього виклику. 6 | 7 | Результати будуть представлені у вигляді масиву, який знаходитиметься **в тому ж порядку**, 8 | що і у вхідному масиві. 9 | 10 | Приклад в EACH-завданні можна записати в такому вигляді: 11 | 12 | ```js 13 | var http = require('http') 14 | , async = require('async'); 15 | async.map(['cat', 'meerkat', 'penguin'], function(item, done){ 16 | var opts = { 17 | hostname: 'http://httpbin.org', 18 | path: '/post', 19 | method: 'POST' 20 | }; 21 | var body = ''; 22 | var req = http.request(opts, function(res){ 23 | res.on('data', function(chunk){ 24 | body += chunk.toString(); 25 | }); 26 | res.on('end', function(){ 27 | return done(null, body); 28 | }); 29 | }); 30 | req.write(item); 31 | req.end(); 32 | }, 33 | function(err, results){ 34 | if (err) return console.log(err); 35 | // Результати - це масив тіл відповідей в тому ж порядку. 36 | }); 37 | ``` 38 | 39 | ## Завдання 40 | 41 | Напишіть програму, яка прийматиме два аргументи командної строки для двох URL. 42 | 43 | Використайте `http.get`, щоб здійснити два GET-запити по цим URL. 44 | 45 | Вам буде потрібно використати `async.map`, а потім `console.log` для результуючого масиву. 46 | -------------------------------------------------------------------------------- /exercises/map/solution/solution.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const { get } = require("http"); 3 | const async = require("async"); 4 | const concat = require("concat-stream"); 5 | const { pipeline } = require("stream"); 6 | 7 | async.map( 8 | process.argv.slice(2), 9 | function (url, done) { 10 | get(url, (res) => { 11 | res.setEncoding("utf-8"); 12 | pipeline( 13 | res, 14 | concat((data) => done(null, data)), 15 | (err) => { 16 | if (err) return done(err); 17 | } 18 | ); 19 | }).on("error", done); 20 | }, 21 | (err, results) => { 22 | if (err) console.error(err); 23 | console.log(results); 24 | } 25 | ); 26 | -------------------------------------------------------------------------------- /exercises/menu.json: -------------------------------------------------------------------------------- 1 | [ 2 | "WATERFALL", 3 | "SERIES OBJECT", 4 | "EACH", 5 | "MAP", 6 | "TIMES", 7 | "REDUCE", 8 | "WHILST" 9 | ] 10 | -------------------------------------------------------------------------------- /exercises/reduce/exercise.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , url = require('url') 3 | , exercise = require('workshopper-exercise')() 4 | , filecheck = require('workshopper-exercise/filecheck') 5 | , execute = require('workshopper-exercise/execute') 6 | , comparestdout = require('workshopper-exercise/comparestdout') 7 | 8 | 9 | // checks that the submission file actually exists 10 | exercise = filecheck(exercise) 11 | 12 | // execute the solution and submission in parallel with spawn() 13 | exercise = execute(exercise) 14 | 15 | // compare stdout of solution and submission 16 | exercise = comparestdout(exercise) 17 | 18 | 19 | // set up the data file to be passed to the submission 20 | exercise.addSetup(function (mode, callback) { 21 | // mode == 'run' || 'verify' 22 | 23 | var numbers = {'one': 1, 'two': 2, 'three': 3} 24 | 25 | this.server = http.createServer(function (req, res) { 26 | var number = url.parse(req.url, true).query.number 27 | if(numbers[number]) 28 | return res.end((numbers[number]).toString()) 29 | res.end("-1") 30 | }) 31 | 32 | this.server.listen(9345, callback) 33 | 34 | var args = [ 'http://localhost:9345' ] 35 | this.submissionArgs = args 36 | this.solutionArgs = args 37 | }) 38 | 39 | 40 | // cleanup for both run and verify 41 | exercise.addCleanup(function (mode, passed, callback) { 42 | // mode == 'run' || 'verify' 43 | 44 | if (!this.server) 45 | return process.nextTick(callback) 46 | 47 | this.server.close(callback) 48 | }) 49 | 50 | 51 | module.exports = exercise 52 | -------------------------------------------------------------------------------- /exercises/reduce/problem.fr.md: -------------------------------------------------------------------------------- 1 | ## Défi 2 | 3 | Écrivez un programme qui reçoit une URL en premier argument de ligne de 4 | commande. 5 | 6 | Pour chaque valeur du tableau suivant, envoyez une requête GET à cette URL, 7 | à l’aide de `http.get()`, en ajoutant un paramètre de **query string** nommé 8 | `number` défini à la valeur en question : 9 | 10 | ```js 11 | ['one', 'two', 'three'] 12 | ``` 13 | 14 | À chaque fois, convertissez le corps de réponse en `Number` et ajoutez-le à la 15 | valeur précédente. 16 | 17 | Faites un `console.log()` de la valeur accumulée. 18 | 19 | ## Conseils 20 | 21 | Utilisez `async.reduce()` : 22 | 23 | https://github.com/caolan/async#reduce 24 | -------------------------------------------------------------------------------- /exercises/reduce/problem.ja.md: -------------------------------------------------------------------------------- 1 | ## チャレンジ 2 | 3 | 最初のコマンドライン引数にURLを受け取るプログラムを作成してください。 4 | 5 | このURLには、以下の配列の値のそれぞれについて、`http.get`を使用してGETリクエストを送信します。 6 | `number`という名前のクエリパラメータを適切な値に設定してください。 7 | 8 | ```js 9 | ['one', 'two', 'three'] 10 | ``` 11 | 12 | 毎回`Number`をレスポンスボディに変換し、前の値を足します。 13 | 計算結果を`console.log`へ出力してください。 14 | 15 | ## ヒント 16 | 17 | `async.reduce`を使用してください。 18 | 19 | https://github.com/caolan/async#reduce 20 | -------------------------------------------------------------------------------- /exercises/reduce/problem.md: -------------------------------------------------------------------------------- 1 | ## Challenge 2 | 3 | Write a program that will receive a URL as the first command line argument. 4 | 5 | To this URL, for each of the values in the following array, send a GET request 6 | using `http.get` with a query parameter named `number` set at the proper value: 7 | 8 | ```js 9 | ['one', 'two', 'three'] 10 | ``` 11 | 12 | Each time, convert the response body to `Number` and add it to the previous value. 13 | `console.log` the final reduced value. 14 | 15 | ## Hints 16 | 17 | Use `async.reduce`: 18 | 19 | https://github.com/caolan/async#reduce 20 | -------------------------------------------------------------------------------- /exercises/reduce/problem.ru.md: -------------------------------------------------------------------------------- 1 | ## Challenge 2 | Напишите программу, которая будет получать URL в качестве первого аргумента командной строки. 3 | 4 | Для каждого из значений в следующем массиве, отправьте GET-запрос на URL, 5 | используя `http.get` с параметром запроса `number`, устанавливая в нужное значение: 6 | 7 | ```js 8 | ['one', 'two', 'three'] 9 | ``` 10 | Каждый раз конвертируйте тело ответа в `Number` и добавляйте его к предыдущему значению. 11 | `console.log` для вывода финального результата. 12 | 13 | ## Советы 14 | 15 | Используйте `async.reduce`: 16 | 17 | https://github.com/caolan/async#reduce 18 | -------------------------------------------------------------------------------- /exercises/reduce/problem.uk.md: -------------------------------------------------------------------------------- 1 | ## Завдання 2 | Напишіть програму, яка буде отримувати URL в якості першого аргументу командної строки. 3 | 4 | Кожне із значень в массиві заданому нижче, використайте для відправки GET-запиту на URL, 5 | за допомогою `http.get` як параметр запиту `number`: 6 | 7 | ```js 8 | ['one', 'two', 'three'] 9 | ``` 10 | Кожного разу конвертуйте тіло відповіді в `Number` і додавайте його до попереднього значення. 11 | `console.log` для виводу фінального результату. 12 | 13 | ## Поради 14 | 15 | Використовуйте `async.reduce`: 16 | 17 | https://github.com/caolan/async#reduce 18 | -------------------------------------------------------------------------------- /exercises/reduce/solution/solution.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const { get } = require("http"); 3 | const async = require("async"); 4 | const { pipeline } = require("stream"); 5 | const concat = require("concat-stream"); 6 | 7 | async.reduce( 8 | ["one", "two", "three"], 9 | 0, 10 | function (memo, item, done) { 11 | get(process.argv[2] + "?number=" + item, (res) => { 12 | res.setEncoding("utf-8"); 13 | pipeline( 14 | res, 15 | concat((data) => { 16 | done(null, Number(data) + memo); 17 | }), 18 | (err) => { 19 | if (err) return done(err); 20 | } 21 | ); 22 | }).on("error", done); 23 | }, 24 | function (err, result) { 25 | if (err) return console.error(err); 26 | console.log(result); 27 | } 28 | ); 29 | -------------------------------------------------------------------------------- /exercises/series_object/exercise.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , url = require('url') 3 | , exercise = require('workshopper-exercise')() 4 | , filecheck = require('workshopper-exercise/filecheck') 5 | , execute = require('workshopper-exercise/execute') 6 | , comparestdout = require('workshopper-exercise/comparestdout') 7 | 8 | 9 | // checks that the submission file actually exists 10 | exercise = filecheck(exercise) 11 | 12 | // execute the solution and submission in parallel with spawn() 13 | exercise = execute(exercise) 14 | 15 | // compare stdout of solution and submission 16 | exercise = comparestdout(exercise) 17 | 18 | 19 | // set up the data file to be passed to the submission 20 | exercise.addSetup(function (mode, callback) { 21 | // mode == 'run' || 'verify' 22 | 23 | this.server = http.createServer(function (req, res) { 24 | if(url.parse(req.url).pathname === '/one') 25 | return res.end('one is smaller than 2'); 26 | res.end('two greater than one'); 27 | }) 28 | 29 | this.server.listen(3131, callback) 30 | 31 | var urls = ['http://localhost:3131/one', 'http://localhost:3131/two'] 32 | this.submissionArgs = urls 33 | this.solutionArgs = urls 34 | }) 35 | 36 | 37 | // cleanup for both run and verify 38 | exercise.addCleanup(function (mode, passed, callback) { 39 | // mode == 'run' || 'verify' 40 | 41 | if (!this.server) 42 | return process.nextTick(callback) 43 | 44 | this.server.close(callback) 45 | }) 46 | 47 | 48 | module.exports = exercise 49 | -------------------------------------------------------------------------------- /exercises/series_object/problem.fr.md: -------------------------------------------------------------------------------- 1 | Dans cet exercice, vous apprendrez à utiliser `async.series()`. 2 | 3 | La principale différence entre `waterfall` et `series` est que le résultat d’une 4 | tâche dans `async.series()` **ne sera pas passé à la suivante** fonction à traiter. 5 | `series` va **récupérer tous les résultats dans un tableau** et passer celui-ci 6 | à terme à la **fonction de rappel optionnelle**, une fois que **toutes les tâches 7 | sont terminées**. Par exemple : 8 | 9 | ```js 10 | async.series([ 11 | function(callback){ 12 | setTimeout(function() { 13 | callback(null, 'one'); 14 | }, 200); 15 | }, 16 | function(callback){ 17 | setTimeout(function() { 18 | callback(null, 'two'); 19 | }, 100); 20 | } 21 | ], 22 | // Fonction de rappel optionnelle 23 | function(err, results){ 24 | // `results` vaut désormais ['one', 'two'] 25 | }); 26 | ``` 27 | 28 | À la place d’un tableau, `async.series()` vous permet d’utiliser un objet, 29 | dont chaque valeur est une fonction de tâche qui sera exécutée, auquel cas 30 | le résultat sera un objet avec des propriétés homonymes qui indiquent les 31 | résultats. L’exemple ci-dessus peut être réécrit comme suit : 32 | 33 | ```js 34 | async.series({ 35 | one: function(done){ 36 | done(null, '1'); 37 | }, 38 | two: function(done){ 39 | done(null, '2'); 40 | } 41 | }, function(err, results){ 42 | console.log(results); 43 | // `results` sera {one: 1, two: 2} 44 | }); 45 | ``` 46 | 47 | ## Défi 48 | 49 | Écrivez un programme qui recevra deux URLs comme premier et second arguments 50 | de la ligne de commande. 51 | 52 | Utilisez `http.get()` pour créer une requête GET sur chaque URL, et passez 53 | leur corps de réponse à la fonction de rappel. 54 | 55 | Utilisez un objet de fonctions de tâche pour `async.series()`, avec des 56 | propriétés nommées `requestOne` et `requestTwo`. 57 | 58 | Faites un `console.log` sur les résultats dans la fonction de rappel finale, 59 | une fois toutes les tâches terminées. 60 | -------------------------------------------------------------------------------- /exercises/series_object/problem.ja.md: -------------------------------------------------------------------------------- 1 | この問題では、 `async.series`を使用する方法を学習します。 2 | 3 | `waterfall`と`series`の主な違いは、一度終了した`async.series`タスク関数からの結果は、 4 | **次に渡されないこと**です。 5 | ` series`は配列としてすべての結果を収集し、**全てのタスクが完了したら**、 6 | **オプションのコールバック**に渡します。 7 | 例えば: 8 | 9 | ```js 10 | async.series([ 11 | function(callback){ 12 | setTimeout(function() { 13 | callback(null, 'one'); 14 | }, 200); 15 | }, 16 | function(callback){ 17 | setTimeout(function() { 18 | callback(null, 'two'); 19 | }, 100); 20 | } 21 | ], 22 | // optional callback 23 | function(err, results){ 24 | // results is now equal to ['one', 'two'] 25 | }); 26 | ``` 27 | 28 | 配列を使う代わりに`async.series`コンテナを使って各プロパティを実行し、 29 | 同じプロパティを持つ結果オブジェクトを作成します。 30 | 上記の例は、以下のように記述できます。 31 | 32 | ```js 33 | async.series({ 34 | one: function(done){ 35 | done(null, '1'); 36 | }, 37 | two: function(done){ 38 | done(null, '2'); 39 | } 40 | }, function(err, results){ 41 | console.log(results); 42 | // results will be {one: 1, two: 2} 43 | }); 44 | ``` 45 | 46 | ## チャレンジ 47 | 48 | 第1、第2引数として2つのURLを受け取るプログラムを書いてください。 49 | 50 | `http.get`を使い、これらのURLにGETリクエストを作成し、コールバックにレスポンスボディを 51 | 渡します。 52 | 53 | プロパティ名に`requestOne`と`requestTwo`を使用し、`async.series`へ 54 | タスク機能のオブジェクトを渡します。 55 | 56 | 全てのタスクが終了したら、シリーズのコールバック結果を`console.log`へ出力します。 57 | -------------------------------------------------------------------------------- /exercises/series_object/problem.md: -------------------------------------------------------------------------------- 1 | In this problem we will learn to use `async.series`. 2 | 3 | The main difference between the `waterfall` and `series` functions is that the 4 | result from a task function in `async.series` **won't be passed along** to the next 5 | function once it completes. `series` will **collect all results as an array** 6 | and pass it to the **optional callback** that runs **once all of the task functions 7 | have completed**. For example: 8 | 9 | ```js 10 | async.series([ 11 | function(callback){ 12 | setTimeout(function() { 13 | callback(null, 'one'); 14 | }, 200); 15 | }, 16 | function(callback){ 17 | setTimeout(function() { 18 | callback(null, 'two'); 19 | }, 100); 20 | } 21 | ], 22 | // optional callback 23 | function(err, results){ 24 | // results is now equal to ['one', 'two'] 25 | }); 26 | ``` 27 | 28 | Instead of using an array as the result container `async.series` can use an 29 | object, running each property and creating a result object with the same 30 | properties. The above example can be written like so: 31 | 32 | ```js 33 | async.series({ 34 | one: function(done){ 35 | done(null, '1'); 36 | }, 37 | two: function(done){ 38 | done(null, '2'); 39 | } 40 | }, function(err, results){ 41 | console.log(results); 42 | // results will be {one: 1, two: 2} 43 | }); 44 | ``` 45 | 46 | ## Challenge 47 | 48 | Write a program that will receive two URLs as the first and second command-line arguments. 49 | 50 | Using `http.get`, create a GET request to these URLs and pass the response body 51 | to the callback. 52 | 53 | Pass in an object of task functions, using the property names `requestOne` and 54 | `requestTwo`, to `async.series`. 55 | 56 | `console.log` the results in the callback for series when all the task functions 57 | have completed. 58 | -------------------------------------------------------------------------------- /exercises/series_object/problem.ru.md: -------------------------------------------------------------------------------- 1 | В этой задаче мы изучим с Вами, как использовать `async.series`. 2 | 3 | Главное отличие между функциями `waterfall` и `series` является то, что результат функции 4 | в `async.series` **не будет передаваться** к следующей функции после ее выполнения. 5 | `series` будет **собирать все результаты в виде массива** и передавать их 6 | **в опциональную функцию обратного вызова**, которая отработает **один раз, 7 | как только все функции выполнятся**. 8 | Например: 9 | 10 | ```js 11 | async.series([ 12 | function(callback){ 13 | setTimeout(function() { 14 | callback(null, 'one'); 15 | }, 200); 16 | }, 17 | function(callback){ 18 | setTimeout(function() { 19 | callback(null, 'two'); 20 | }, 100); 21 | } 22 | ], 23 | // Опциональная функция обратного вызова. 24 | function(err, results){ 25 | // Результат теперь будет равен ['one', 'two'] 26 | }); 27 | ``` 28 | Вместо использования массива, как контейнера для результата `async.series` может 29 | использовать объект, запуская каждое свойство и создавая объект в качестве 30 | результата с теми же свойствами. Пример выше может быть переписан как: 31 | 32 | ```js 33 | async.series({ 34 | one: function(done){ 35 | done(null, '1'); 36 | }, 37 | two: function(done){ 38 | done(null, '2'); 39 | } 40 | }, function(err, results){ 41 | console.log(results); 42 | // Результатом будет {one: 1, two: 2} 43 | }); 44 | ``` 45 | 46 | ## Задание 47 | 48 | Напишите программу, которая принимает два URL, как первый и второй аргумент 49 | командной строки. 50 | 51 | Используя `http.get`, создайте GET запрос на эти URL и передайте тело ответа в 52 | функцию обратного вызова. 53 | 54 | Передайте в функцию `async.series` объект, используя свойства `requestOne` и 55 | `requestTwo`. 56 | 57 | Используйте `console.log` для вывода результатов, когда все функции выполнятся. 58 | -------------------------------------------------------------------------------- /exercises/series_object/problem.uk.md: -------------------------------------------------------------------------------- 1 | В цьому завданні ми навчимося використовувати `async.series`. 2 | 3 | Основна відмінність між функціями `waterfall` і `series` в тому, що результат функції 4 | в `async.series` **не буде передаватися** до наступної функції після її виконання. 5 | `series` буде **збирати всі результати у вигляді масиву** і передавати їх 6 | **в опціональну функцію зворотнього виклику**, яка відпрацює **за один раз, 7 | як тільки всі функції виконаються**. 8 | Наприклад: 9 | 10 | ```js 11 | async.series([ 12 | function(callback){ 13 | setTimeout(function() { 14 | callback(null, 'one'); 15 | }, 200); 16 | }, 17 | function(callback){ 18 | setTimeout(function() { 19 | callback(null, 'two'); 20 | }, 100); 21 | } 22 | ], 23 | // Опціональна функція зворотнього виклику. 24 | function(err, results){ 25 | // Результат тепер буде рівний ['one', 'two'] 26 | }); 27 | ``` 28 | Замість використання масиву, як вмістилища для результату `async.series` можна 29 | використовувати об'єкт, запускаючи кожну властивість і створюючи об'єкт в якості 30 | результату з тими ж властивостями. Приклад вище можна переписати таким чином: 31 | 32 | ```js 33 | async.series({ 34 | one: function(done){ 35 | done(null, '1'); 36 | }, 37 | two: function(done){ 38 | done(null, '2'); 39 | } 40 | }, function(err, results){ 41 | console.log(results); 42 | // Результатом буде {one: 1, two: 2} 43 | }); 44 | ``` 45 | 46 | ## Завдання 47 | 48 | Напишіть програму, яка приймає два URL, в якості першого і другого аргументів 49 | командної строки. 50 | 51 | Використовуючи `http.get`, створіть GET запит на ці URL і передайте тіло відповіді в 52 | функцію зворотнього виклику. 53 | 54 | Передайте в функцію `async.series` об'єкт, використовуючи властивості `requestOne` і 55 | `requestTwo`. 56 | 57 | Використайте `console.log` для виводу результатів, коли всі функції виконаються. 58 | -------------------------------------------------------------------------------- /exercises/series_object/solution/solution.js: -------------------------------------------------------------------------------- 1 | const { get } = require("http"); 2 | const { pipeline } = require("stream"); 3 | const concat = require("concat-stream"); 4 | const async = require("async"); 5 | 6 | async.series( 7 | { 8 | requestOne: function (done) { 9 | fetchURL(process.argv[2], done); 10 | }, 11 | requestTwo: function (done) { 12 | fetchURL(process.argv[3], done); 13 | }, 14 | }, 15 | function (err, results) { 16 | if (err) return console.error(err); 17 | console.log(results); 18 | } 19 | ); 20 | 21 | function fetchURL(url, callback) { 22 | get(url, (res) => { 23 | res.setEncoding("utf-8"); 24 | pipeline( 25 | res, 26 | concat((data) => { 27 | callback(null, data); 28 | }), 29 | (err) => { 30 | if (err) return callback(err); 31 | } 32 | ); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /exercises/times/exercise.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , async = require('async') 3 | , exercise = require('workshopper-exercise')() 4 | , filecheck = require('workshopper-exercise/filecheck') 5 | , execute = require('workshopper-exercise/execute') 6 | , comparestdout = require('workshopper-exercise/comparestdout') 7 | 8 | 9 | // checks that the submission file actually exists 10 | exercise = filecheck(exercise) 11 | 12 | // execute the solution and submission in parallel with spawn() 13 | exercise = execute(exercise) 14 | 15 | // compare stdout of solution and submission 16 | exercise = comparestdout(exercise) 17 | 18 | 19 | // set up the data file to be passed to the submission 20 | exercise.addSetup(function (mode, callback) { 21 | // mode == 'run' || 'verify' 22 | 23 | var ports = [9345, 9346], servers = this.servers = [], users = [[], []], 24 | index = 0 25 | 26 | function startServer(serverUsers, serverCallback) { 27 | var count = 0 28 | var server = http.createServer(function (req, res) { 29 | handleRequests(req, res, serverUsers) 30 | }).listen(ports[index++], serverCallback) 31 | servers.push(server); 32 | } 33 | 34 | this.submissionArgs = [ "localhost", ports[0] ] 35 | this.solutionArgs = [ "localhost", ports[1] ] 36 | 37 | async.each(users, startServer, callback) 38 | 39 | function handleRequests(req, res, users) { 40 | var body = ""; 41 | if (req.method.toLowerCase() === 'post') { 42 | req.on('data', function(chunk){ 43 | body += chunk.toString(); 44 | }); 45 | req.on('end', function(){ 46 | users.push(JSON.parse(body)); 47 | res.end(); 48 | }); 49 | } else { 50 | 51 | users.sort(function(a, b) { return a.user_id - b.user_id; }); 52 | 53 | res.end(JSON.stringify({'users': users})); 54 | } 55 | } 56 | }) 57 | 58 | 59 | // cleanup for both run and verify 60 | exercise.addCleanup(function (mode, passed, callback) { 61 | // mode == 'run' || 'verify' 62 | 63 | if (!this.servers.length) 64 | return process.nextTick(callback) 65 | 66 | function closeServer(server, serverCallback) { 67 | server.close(serverCallback); 68 | } 69 | 70 | async.each(this.servers, closeServer, callback); 71 | }) 72 | 73 | 74 | module.exports = exercise 75 | -------------------------------------------------------------------------------- /exercises/times/problem.fr.md: -------------------------------------------------------------------------------- 1 | # Défi 2 | 3 | Écrivez un programme qui reçoit deux arguments de ligne de commande : 4 | un nom d’hôte et un numéro de port. Utilisez `http.request()` pour 5 | envoyer une requête POST sur : 6 | 7 | ```js 8 | url + '/users/create' 9 | ``` 10 | 11 | …avec un corps de requête contenant un objet « JSONifié » (`JSON.stringify()`) : 12 | 13 | ```js 14 | {"user_id": 1} 15 | ``` 16 | 17 | Faites ça cinq fois, en incrémentant le `user_id` à chaque tour, en 18 | commençant par 1. 19 | 20 | Une fois que toutes les requêtes sont terminées, envoyez une requête GET à : 21 | 22 | ```js 23 | url + '/users' 24 | ``` 25 | 26 | …et affichez le corps de réponse avec `console.log`. 27 | 28 | ## Conseils 29 | 30 | Dans cet exercice, vous devez coordonner plusieurs opérations asynchrones. 31 | 32 | Utilisez `async.series()` avec un objet de tâches. L’une de ces tâches 33 | devra utiliser `async.times()` pour envoyer les requêtes POST à l’aide 34 | de `http.request()`. L’autre fera juste une requête GET. 35 | 36 | Vous trouverez davantage d’informations sur `async.times()` ici : 37 | 38 | https://github.com/caolan/async#times 39 | -------------------------------------------------------------------------------- /exercises/times/problem.ja.md: -------------------------------------------------------------------------------- 1 | # チャレンジ 2 | 3 | コマンドラインから2つの引数(hostname, port)を受け取るプログラムを作成してください。 4 | `http.request`を使って下記へPOSTしてください。 5 | 6 | ```js 7 | url + '/users/create' 8 | ``` 9 | 10 | bodyには`JSON.stringify`を含めてください。 11 | 12 | ```js 13 | {"user_id": 1} 14 | ``` 15 | 16 | これを5回繰り返します。`user_id`プロパティは1から始めてインクリメントしてください。 17 | 18 | リクエスト終了後、GET requestを送ってください: 19 | 20 | ```js 21 | url + '/users' 22 | ``` 23 | 24 | そしてGET requestのresponse bodyを`console.log`へ出力してください。 25 | 26 | ## ヒント 27 | 28 | この問題では、いくつか非同期操作をコーディネートする必要があります。 29 | 30 | `async.series`を使用し、`Object`へ渡します。 31 | タスク機能の一つは`http.request`を`使用してPOSTリクエストを送信するために、 `async.times`を使用する必要があります。 32 | 33 | `async.times`については下記に詳しく書いてあります。 34 | 35 | https://github.com/caolan/async#times 36 | -------------------------------------------------------------------------------- /exercises/times/problem.md: -------------------------------------------------------------------------------- 1 | # Challenge 2 | 3 | Write a program that will receive two command line arguments containing 4 | the hostname and port. Using `http.request` send a POST request to 5 | 6 | ```js 7 | url + '/users/create' 8 | ``` 9 | 10 | with the body containing a `JSON.stringify`'ed object: 11 | 12 | ```js 13 | {"user_id": 1} 14 | ``` 15 | 16 | Do this five times with each time the `user_id` property being incremented, 17 | starting at 1. 18 | 19 | Once these requests are done, send a GET request to: 20 | 21 | ```js 22 | url + '/users' 23 | ``` 24 | 25 | and `console.log` the response body for the GET request. 26 | 27 | ## Hints 28 | 29 | In this problem, you will need to co-ordinate a few async operations. 30 | 31 | Use `async.series` for this and pass in an `Object`. One of the task 32 | functions will need to use `async.times` to send POST requests using 33 | `http.request`. The other will then do the GET request. 34 | 35 | You can read more about `async.times` here: 36 | 37 | https://github.com/caolan/async#times 38 | -------------------------------------------------------------------------------- /exercises/times/problem.ru.md: -------------------------------------------------------------------------------- 1 | # Задача 2 | 3 | Напишите программу, которая будет принимать два аргумента в командной строке, 4 | содержащие хост и порт. Используйте `http.request` и отправте POST-запрос на 5 | 6 | ```js 7 | url + '/users/create' 8 | ``` 9 | с телом, содержащим `JSON.stringify` объект: 10 | 11 | ```js 12 | {"user_id": 1} 13 | ``` 14 | 15 | Сделайте это в пять раз и каждый раз увеличивайте свойство `user_id`, начиная с 1. 16 | 17 | Когда запросы будут завершены, отправьте GET-запрос на: 18 | 19 | ```js 20 | url + '/users' 21 | ``` 22 | 23 | используйте `console.log` на тело ответа от GET-запроса. 24 | 25 | ## Советы 26 | 27 | В этой задаче, вам необходимо будет координировать несколько асинхронное операций. 28 | 29 | Используйте `async.series` для этого и передать в `Object`. В одной из функций 30 | нужно использовать `async.times`, чтобы отправить POST-request используя `http.request`. 31 | Другая будет делать GET-запрос. 32 | 33 | Вы можете прочитать больше о функции `async.times` здесь: 34 | 35 | https://github.com/caolan/async#times 36 | -------------------------------------------------------------------------------- /exercises/times/problem.uk.md: -------------------------------------------------------------------------------- 1 | # Завдання 2 | 3 | Напишіть програму, яка приймає два аргументи командної строки, які 4 | містять хост і порт. Використайте `http.request` та відправте POST-запит на 5 | 6 | ```js 7 | url + '/users/create' 8 | ``` 9 | з тілом, яке містить `JSON.stringify` об'єкт: 10 | 11 | ```js 12 | {"user_id": 1} 13 | ``` 14 | 15 | Зробіть це п'ять разів, щоразу збільшуючи властивість `user_id`, починаючи з 1. 16 | 17 | Коли запити будуть завершені, відправте GET-запит на: 18 | 19 | ```js 20 | url + '/users' 21 | ``` 22 | 23 | використайте `console.log` на тіло відповіді від GET-запиту. 24 | 25 | ## Поради 26 | 27 | В цьому завданні, вам буде необхідно координувати декілька асинхронних операцій. 28 | 29 | Використайте `async.series` для цього і передайте в `Object`. В одній з функцій 30 | потрібно використати `async.times`, щоб відправити POST-request використовуючи `http.request`. 31 | Інша буде робити GET-запит. 32 | 33 | Ви можете прочитать більше про `async.times` тут: 34 | 35 | https://github.com/caolan/async#times 36 | -------------------------------------------------------------------------------- /exercises/times/solution/solution.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const { get, request } = require("http"); 3 | const async = require("async"); 4 | const { pipeline } = require("stream"); 5 | const concat = require("concat-stream"); 6 | 7 | async.series( 8 | { 9 | create: function (done) { 10 | async.times( 11 | 5, 12 | function (n, next) { 13 | _addUser(++n, next); 14 | }, 15 | function (err) { 16 | if (err) return done(err); 17 | done(null); 18 | } 19 | ); 20 | }, 21 | users: function (done) { 22 | const opts = { 23 | hostname: process.argv[2], 24 | port: process.argv[3], 25 | pathname: "/users", 26 | }; 27 | get(opts, (res) => { 28 | res.setEncoding("utf-8"); 29 | pipeline( 30 | res, 31 | concat((data) => { 32 | done(null, data); 33 | }), 34 | (err) => { 35 | if (err) return done(err); 36 | } 37 | ); 38 | }).on("error", done); 39 | }, 40 | }, 41 | function (err, results) { 42 | if (err) return console.error(err); 43 | console.log(results.users); 44 | } 45 | ); 46 | 47 | function _addUser(user_id, next) { 48 | const postData = JSON.stringify({ user_id }); 49 | const opts = { 50 | hostname: process.argv[2], 51 | port: process.argv[3], 52 | pathname: "/users/create", 53 | method: "POST", 54 | headers: { 55 | "Content-Length": postData.length, 56 | }, 57 | }; 58 | const req = request(opts, (res) => { 59 | res 60 | .on("data", (chunk) => {}) 61 | .on("end", () => { 62 | next(null, "created"); 63 | }) 64 | .on("error", next); 65 | }).on("error", next); 66 | req.write(postData); 67 | req.end(); 68 | } 69 | -------------------------------------------------------------------------------- /exercises/waterfall/exercise.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , path = require('path') 3 | , exercise = require('workshopper-exercise')() 4 | , filecheck = require('workshopper-exercise/filecheck') 5 | , execute = require('workshopper-exercise/execute') 6 | , comparestdout = require('workshopper-exercise/comparestdout') 7 | 8 | 9 | // checks that the submission file actually exists 10 | exercise = filecheck(exercise) 11 | 12 | // execute the solution and submission in parallel with spawn() 13 | exercise = execute(exercise) 14 | 15 | // compare stdout of solution and submission 16 | exercise = comparestdout(exercise) 17 | 18 | 19 | // set up the data file to be passed to the submission 20 | exercise.addSetup(function (mode, callback) { 21 | // mode == 'run' || 'verify' 22 | 23 | this.server = http.createServer(function (req, res) { 24 | res.end('boom!') 25 | }) 26 | 27 | this.server.listen(9345, callback) 28 | 29 | var filePath = path.join(__dirname, 'url.txt'); 30 | this.submissionArgs = [ filePath ] 31 | this.solutionArgs = [ filePath ] 32 | }) 33 | 34 | 35 | // cleanup for both run and verify 36 | exercise.addCleanup(function (mode, passed, callback) { 37 | // mode == 'run' || 'verify' 38 | 39 | if (!this.server) 40 | return process.nextTick(callback) 41 | 42 | this.server.close(callback) 43 | }) 44 | 45 | 46 | module.exports = exercise 47 | -------------------------------------------------------------------------------- /exercises/waterfall/problem.fr.md: -------------------------------------------------------------------------------- 1 | Dans Node.js comme dans les navigateurs, il y a trois façons de faire du 2 | JavaScript **asynchrone**. 3 | 4 | La première approche nous emmène tout droit dans l’**Enfer des Callbacks**. 5 | Il est possible de mitiger ce phénomène en suivant les conseils listés ici : 6 | 7 | http://callbackhell.com 8 | 9 | La deuxième méthode consiste à utiliser une implémentation des *Promesses*. 10 | Utiliser des promesses peut simplifier votre code mais introduit un niveau 11 | d’abstraction supplémentaire. 12 | 13 | Finalement, vous pouvez utiliser le module `async` de Caolan McMahon. Avec 14 | **async**, vous écrivez toujours des fonctions de rappel, mais sans tomber 15 | dans l’enfer des callbacks ni ajouter une nouvelle couche d’abstraction. 16 | 17 | Il arrive bien souvent que vous ayez besoin d’exécuter de multiples appels 18 | asynchrones l’un après l’autre, en passant à chaque appel le résultat du 19 | précédent. C’est facile à réaliser avec l’aide de `async.waterfall()`. 20 | 21 | Par exemple, le code qui suit fait deux traitements consécutifs : 22 | 23 | 1) Une requête GET à `http://localhost:3131` dans la première fonction de la 24 | cascade ; 25 | 2) Un `JSON.parse` sur le corps de réponse obtenu, passé par la première fonction 26 | de la cascade via sa propre fonction de rappel, et transmis en premier argument 27 | à la deuxième fonction de la cascade ; on en extrait alors la propriété `port` 28 | et on fait une nouvelle requête GET avec. 29 | 30 | ```js 31 | var http = require('http') 32 | , async = require('async'); 33 | 34 | async.waterfall([ 35 | // Première fonction de cascade ; `cb` est le rappel de continuation 36 | // passé par `async`, à appeler quand on aura terminé cette étape. 37 | function(cb){ 38 | // La réponse sera un encodage JSON de l’objet `{port: 3132}` 39 | fetchURL("http://localhost:3131", cb); 40 | }, 41 | 42 | // Deuxième fonction de cascade : la valeur passée par la première à 43 | // son `cb` est transmise en 1er argument, et ce nouveau `cb` est 44 | // toujours une fonction de continuation passée par `async`. 45 | function(body, cb){ 46 | var port = JSON.parse(body).port; 47 | fetchURL("http://localhost:" + port, cb); 48 | } 49 | ], 50 | // Gestionnaire d’erreur / de résultat global à la cascade. 51 | function(err, result){ 52 | if (err) return console.error(err); 53 | console.log(result); 54 | }); 55 | 56 | function fetchURL(url, cb) { 57 | var body = ''; 58 | http.get(url, function(res){ 59 | res.on('data', function(chunk){ 60 | body += chunk.toString(); 61 | }); 62 | res.on('end', function(){ 63 | cb(null, body); 64 | }); 65 | }).on('error', function(err) { 66 | cb(err); 67 | }); 68 | } 69 | ``` 70 | 71 | ## Défi 72 | 73 | Pour cet exercice vous devrez écrire un programme qui lise le contenu 74 | d’un fichier, de façon asynchrone évidemment. 75 | 76 | Le chemin de ce fichier vous sera fourni en premier argument de la ligne 77 | de commande de votre programme (`process.argv[2]`). 78 | 79 | Le fichier contient une unique URL. Utilisez `http.get` pour créer une 80 | requête GET vers cette URL et faire un `console.log` sur le corps de réponse. 81 | -------------------------------------------------------------------------------- /exercises/waterfall/problem.ja.md: -------------------------------------------------------------------------------- 1 | Node.jsとブラウザで**非同期**JavaScriptを行うには、次の3つの方法があります。 2 | 3 | 最初の方法は、**コールバック地獄**と呼ぶものにつながります。 4 | コー​​ルバック地獄は以下のサイトでのヒントに従うことによって最小限に抑えることができます。 5 | 6 | http://callbackhell.com. 7 | 8 | 別の方法は、 `Promise`パッケージを使用することです。 9 | `Promise`パッケージを使用すると、コードは簡素化しますが、抽象化のための別のレイヤーを追加します。 10 | 11 | 最後の方法は、Caolan McMahonが作成した `async`パッケージを使用することです。 12 | `async`パッケージを使用すると、コールバックは書きますが、コールバック地獄に落ちたり、 13 | `Promise`パッケージを使って抽象化のための別のレイヤーを追加することはありません。 14 | 15 | 多くの場合、先に実行した非同期呼び出しの実行結果によって、 16 | 複数の非同期呼び出しを連続して実行する必要あります。 17 | 18 | たとえば、次のようなコードです: 19 | 20 | 1) 最初のウォーターフォール関数で`http://localhost:3131`をGET request します。 21 | 22 | 2) レスポンスボディは、コールバックを介して次のウォーターフォール関数の引数として渡されます。 23 | ウォーターフォールの次の機能はパラメーターとしてボディを受け入れ、 24 | `port`プロパティを取得するため`JSON.parse`し、別のGET requestをします。 25 | 26 | ```js 27 | var http = require('http') 28 | , async = require('async'); 29 | 30 | async.waterfall([ 31 | function(cb){ 32 | var body = ''; 33 | // response is JSON encoded object like the following {port: 3132} 34 | http.get("http://localhost:3131", function(res){ 35 | res.on('data', function(chunk){ 36 | body += chunk.toString(); 37 | }); 38 | res.on('end', function(){ 39 | cb(null, body); 40 | }); 41 | }).on('error', function(err) { 42 | cb(err); 43 | }); 44 | }, 45 | 46 | function(body, cb){ 47 | var port = JSON.parse(body).port; 48 | var body = ''; 49 | http.get("http://localhost:" + port, function(res){ 50 | res.on('data', function(chunk){ 51 | body += chunk.toString(); 52 | }); 53 | res.on('end', function(){ 54 | cb(null, body); 55 | }); 56 | }).on('error', function(err) { 57 | cb(err); 58 | }); 59 | } 60 | ], function(err, result){ 61 | if (err) return console.error(err); 62 | console.log(result); 63 | }); 64 | ``` 65 | 66 | ## チャレンジ 67 | 68 | この問題では、最初のファイルの内容を読み取るプログラムを作成する必要があります。 69 | 70 | パスはプログラムの最初のコマンドライン引数として提供されます 71 | (`process.argv [ 2 ]` ) 。 72 | 73 | ファイルには、単一のURLが含まれています。 74 | `http.get`を使用して、このURLにGETリクエストを作成し、 レスポンスボディを`console.log`へ出力します 。 75 | -------------------------------------------------------------------------------- /exercises/waterfall/problem.md: -------------------------------------------------------------------------------- 1 | In Node.js and browsers there are three ways to do **asynchronous** JavaScript. 2 | 3 | The first way leads to what we call **Callback Hell**. Callback Hell 4 | can be minimized by following the tips at: 5 | 6 | http://callbackhell.com. 7 | 8 | Another method is to use a `Promise` package. Using promises will simplify your 9 | code but it also adds another layer of abstraction. 10 | 11 | The last method is by using the `async` package by Caolan McMahon. With **async** 12 | we are still writing callbacks but without falling into the callback hell or 13 | adding another layer of abstraction with promises. 14 | 15 | More often than not you will need to do multiple asynchronous calls one after 16 | the other with each call dependent on the result of previous asynchronous call. 17 | We can do this with the help of `async.waterfall`. 18 | 19 | For example the following code will: 20 | 21 | 1) do a GET request to `http://localhost:3131` in the first waterfall function. 22 | 2) The response body is passed as an argument to the next waterfall function via 23 | the callback. The second function in the waterfall accepts the body as a 24 | parameter and `JSON.parse`'s it to get to the `port` property then it does 25 | another GET request. 26 | 27 | ```js 28 | var http = require('http') 29 | , async = require('async'); 30 | 31 | async.waterfall([ 32 | function(cb){ 33 | var body = ''; 34 | // response is JSON encoded object like the following {port: 3132} 35 | http.get("http://localhost:3131", function(res){ 36 | res.on('data', function(chunk){ 37 | body += chunk.toString(); 38 | }); 39 | res.on('end', function(){ 40 | cb(null, body); 41 | }); 42 | }).on('error', function(err) { 43 | cb(err); 44 | }); 45 | }, 46 | 47 | function(body, cb){ 48 | var port = JSON.parse(body).port; 49 | var body = ''; 50 | http.get("http://localhost:" + port, function(res){ 51 | res.on('data', function(chunk){ 52 | body += chunk.toString(); 53 | }); 54 | res.on('end', function(){ 55 | cb(null, body); 56 | }); 57 | }).on('error', function(err) { 58 | cb(err); 59 | }); 60 | } 61 | ], function(err, result){ 62 | if (err) return console.error(err); 63 | console.log(result); 64 | }); 65 | ``` 66 | 67 | ## Challenge 68 | 69 | In this problem you will need to write a program that first reads the contents 70 | of a file. 71 | 72 | The path will be provided as the first command-line argument to your program 73 | (i.e. `process.argv[2]`). 74 | 75 | The file will contain a single URL. Using `http.get`, create a GET request to 76 | this URL and `console.log` the response body. 77 | -------------------------------------------------------------------------------- /exercises/waterfall/problem.ru.md: -------------------------------------------------------------------------------- 1 | В Node.js и браузерах есть 3 способа реализовать **асинхронный** JavaScript. 2 | 3 | Первый путь приводит к тому, что мы называем **Callback Hell**. 4 | Callback Hell можно свести к минимуму, следуя следующим советам: 5 | http://callbackhell.com. 6 | 7 | Другой метод заключается в использовании пакета `Promise`. Используя `Promise` 8 | можно упростить код, но он также добавляет еще один уровень абстракции. 9 | 10 | Последний метод заключается в использовании пакета `async`, созданного Caolan McMahon. 11 | Благодаря **async** мы все еще пишем функции обратного вызова, но без использования 12 | Callback Hell или добавления другого уровня абстракции от Promise. 13 | 14 | Чаще всего вам нужно будет сделать несколько асинхронных вызовов один за другим и 15 | каждый вызов будет в зависимости от результата предыдущего асинхронного вызова. 16 | Мы можем сделать это с помощью `async.waterfall`. 17 | 18 | Для примера рассмотрим следующий код: 19 | 20 | 1) Делаем запрос GET на `http://localhost:3131` в функции waterfall. 21 | 2) Тело ответ передается в качестве аргумента к следующей функции waterfall через 22 |    обратный вызов. Вторая функция в waterfall принимает тело как 23 |    параметр и использует `JSON.parse`, чтобы получить свойство `port`, 24 |    чтобы сделать другой GET запрос. 25 | 26 | ```js 27 | var http = require('http') 28 | , async = require('async'); 29 | 30 | async.waterfall([ 31 | function(cb){ 32 | var body = ''; 33 | // ответ - это закодируемый JSON-объект, как следующий пример {порт: 3132} 34 | http.get("http://localhost:3131", function(res){ 35 | res.on('data', function(chunk){ 36 | body += chunk.toString(); 37 | }); 38 | res.on('end', function(){ 39 | cb(null, body); 40 | }); 41 | }).on('error', function(err) { 42 | cb(err); 43 | }); 44 | }, 45 | 46 | function(body, cb){ 47 | var port = JSON.parse(body).port; 48 | var body = ''; 49 | http.get("http://localhost:" + port, function(res){ 50 | res.on('data', function(chunk){ 51 | body += chunk.toString(); 52 | }); 53 | res.on('end', function(){ 54 | cb(null, body); 55 | }); 56 | }).on('error', function(err) { 57 | cb(err); 58 | }); 59 | } 60 | ], function(err, result){ 61 | if (err) return console.error(err); 62 | console.log(result); 63 | }); 64 | ``` 65 | 66 | ## Задание 67 | 68 | В этой задаче вам нужно написать программу, которая будет считывать содержимое файла. 69 | 70 | Путь к файлу будет передан, как первый аргумент в командной строке 71 | (т.е `process.argv[2]`). 72 | 73 | Файл будет содержать один URL. Используйте `http.get`, чтобы сделать GET запрос на 74 | этот URL и напишите `console.log`, чтобы вывести тело ответа. 75 | -------------------------------------------------------------------------------- /exercises/waterfall/problem.uk.md: -------------------------------------------------------------------------------- 1 | В Node.js та бра́узерах є 3 способи реалізувати **асинхронний** JavaScript. 2 | 3 | Перший призводить до того, що ми називаємо **Callback Hell**. 4 | Callback Hell можна звести до мінімуму, якщо слідувати порадам: 5 | http://callbackhell.com. 6 | 7 | Другой заключаєтся в використанні пакету `Promise`. Використовуючи `Promise` 8 | можна спростити код, але він також добавляє ще один рівень абстракції. 9 | 10 | Останній метод заключаєтся у використанні пакету `async`, створеного Caolan McMahon. 11 | Завдяки **async** ми все ще пишемо функції зворотнього виклику, але без використання 12 | Callback Hell чи додавання додаткового рівня абстракції від Promise. 13 | 14 | Найчастіше у вас буде необхідність робити декілька асинхронних викликів один за одним і 15 | кожен виклик буде в залежності від результату попереднього асинхронного виклику. 16 | Ми можемо зробити це за допомогою `async.waterfall`. 17 | 18 | Для прикладу розглянемо наступний код: 19 | 20 | 1) Робимо запит GET на `http://localhost:3131` в функції waterfall. 21 | 2) Тіло-відповідь передаєтся в якості аргументу до наступної функції waterfall через 22 |    зворотній виклик. Друга функція в waterfall приймає тіло як 23 |    параметр і використовує `JSON.parse`, щоб отримати властивість `port` 24 |    та зробити інший GET запит. 25 | 26 | ```js 27 | var http = require('http') 28 | , async = require('async'); 29 | 30 | async.waterfall([ 31 | function(cb){ 32 | var body = ''; 33 | // відповідь - це закодований JSON-об'єкт, як наступний приклад {порт: 3132} 34 | http.get("http://localhost:3131", function(res){ 35 | res.on('data', function(chunk){ 36 | body += chunk.toString(); 37 | }); 38 | res.on('end', function(){ 39 | cb(null, body); 40 | }); 41 | }).on('error', function(err) { 42 | cb(err); 43 | }); 44 | }, 45 | 46 | function(body, cb){ 47 | var port = JSON.parse(body).port; 48 | var body = ''; 49 | http.get("http://localhost:" + port, function(res){ 50 | res.on('data', function(chunk){ 51 | body += chunk.toString(); 52 | }); 53 | res.on('end', function(){ 54 | cb(null, body); 55 | }); 56 | }).on('error', function(err) { 57 | cb(err); 58 | }); 59 | } 60 | ], function(err, result){ 61 | if (err) return console.error(err); 62 | console.log(result); 63 | }); 64 | ``` 65 | 66 | ## Завдання 67 | 68 | В цьому завданні вам потрібно написати програму, яка буде зчитувати вміст файлу. 69 | 70 | Шлях до файлу буде переданий, як перший аргумент командної строки 71 | (тобто `process.argv[2]`). 72 | 73 | Файл буде містити один URL. Використайте `http.get`, щоб зробити GET запит на 74 | цей URL і `console.log`, щоб вивести тіло відповіді. 75 | -------------------------------------------------------------------------------- /exercises/waterfall/solution/solution.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const { get } = require("http"); 3 | const async = require("async"); 4 | const concat = require("concat-stream"); 5 | const fs = require("fs"); 6 | const { pipeline } = require("stream"); 7 | 8 | async.waterfall( 9 | [ 10 | function (done) { 11 | pipeline( 12 | fs.createReadStream(process.argv[2], "utf-8"), 13 | concat((data) => done(null, data)), 14 | (err) => { 15 | if (err) return done(err); 16 | } 17 | ); 18 | }, 19 | function (url, done) { 20 | get(url, (res) => { 21 | res.setEncoding("utf-8"); 22 | pipeline( 23 | res, 24 | concat((data) => done(null, data)), 25 | (err) => { 26 | if (err) return done(err); 27 | } 28 | ); 29 | }).on("error", done); 30 | }, 31 | ], 32 | function (err, result) { 33 | if (err) return console.error(err); 34 | console.log(result); 35 | } 36 | ); 37 | -------------------------------------------------------------------------------- /exercises/waterfall/url.txt: -------------------------------------------------------------------------------- 1 | http://localhost:9345 2 | -------------------------------------------------------------------------------- /exercises/whilst/exercise.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , async = require('async') 3 | , exercise = require('workshopper-exercise')() 4 | , filecheck = require('workshopper-exercise/filecheck') 5 | , execute = require('workshopper-exercise/execute') 6 | , comparestdout = require('workshopper-exercise/comparestdout') 7 | 8 | 9 | // checks that the submission file actually exists 10 | exercise = filecheck(exercise) 11 | 12 | // execute the solution and submission in parallel with spawn() 13 | exercise = execute(exercise) 14 | 15 | // compare stdout of solution and submission 16 | exercise = comparestdout(exercise) 17 | 18 | 19 | // set up the data file to be passed to the submission 20 | exercise.addSetup(function (mode, callback) { 21 | // mode == 'run' || 'verify' 22 | 23 | var max = Math.min(2, Math.floor(Math.random() * 5) + 1) 24 | 25 | var ports = [9345, 9346], servers = this.servers = [] 26 | 27 | function startServer(port, serverCallback) { 28 | var count = 0 29 | var server = http.createServer(function (req, res) { 30 | if (count >= max) { 31 | res.end('meerkat') 32 | } else { 33 | ++count; 34 | res.end() 35 | } 36 | }).listen(port, serverCallback); 37 | servers.push(server); 38 | } 39 | 40 | this.submissionArgs = [ "http://localhost:" + ports[0] ] 41 | this.solutionArgs = [ "http://localhost:" + ports[1] ] 42 | 43 | async.each(ports, startServer, callback) 44 | }) 45 | 46 | 47 | // cleanup for both run and verify 48 | exercise.addCleanup(function (mode, passed, callback) { 49 | // mode == 'run' || 'verify' 50 | 51 | if (!this.servers.length) 52 | return process.nextTick(callback) 53 | 54 | function closeServer(server, serverCallback) { 55 | server.close(serverCallback); 56 | } 57 | 58 | async.each(this.servers, closeServer, callback); 59 | }) 60 | 61 | 62 | module.exports = exercise 63 | -------------------------------------------------------------------------------- /exercises/whilst/problem.fr.md: -------------------------------------------------------------------------------- 1 | ## Défi 2 | 3 | Écrivez un programme qui reçoit une URL en argument de ligne de commande. 4 | 5 | Utilisez `async.whilst()` et `http.get()` pour envoyer des requêtes GET à 6 | cette URL jusqu’à ce que le corps de réponse contienne `"meerkat"`. 7 | 8 | Faites un `console.log()` du nombre de requêtes GET qu’il vous aura fallu pour 9 | obtenir le texte "meerkat". 10 | 11 | ## Conseils 12 | 13 | `String.prototype.trim()` est votre ami. 14 | 15 | Vous trouverez la documentation de `async.whilst()` ici : 16 | 17 | https://github.com/caolan/async#whilst 18 | -------------------------------------------------------------------------------- /exercises/whilst/problem.ja.md: -------------------------------------------------------------------------------- 1 | ## チャレンジ 2 | 3 | コマンドライン引数にURLを1つ受け取るプログラムを作成してください。 4 | 5 | `async.whilst`と`http.get`を使用して、レスポンスボディは、文字列`"meerkat"`を 6 | 含むまでこのURLにGETリクエストを送信します。 7 | 8 | "meerkat"の文字列を取得するために必要なGET Requestの量を`console.log`へ出力してください。 9 | 10 | ## ヒント 11 | 12 | `String.prototype.trim()`が力になるでしょう。 13 | 14 | `async.whilst()`のドキュメントはこちらにあります: 15 | 16 | https://github.com/caolan/async#whilst 17 | -------------------------------------------------------------------------------- /exercises/whilst/problem.md: -------------------------------------------------------------------------------- 1 | ## Challenge 2 | 3 | Write a program that will receive a single command line argument to a URL. 4 | 5 | Using `async.whilst` and `http.get`, send GET requests to this URL until 6 | the response body contains the string `"meerkat"`. 7 | 8 | `console.log` the amount of GET requests needed to retrieve the "meerkat" string. 9 | 10 | ## Hints 11 | 12 | `String.prototype.trim()` is your friend. 13 | 14 | You can get documentation on `async.whilst()` here: 15 | 16 | https://github.com/caolan/async#whilst 17 | -------------------------------------------------------------------------------- /exercises/whilst/problem.ru.md: -------------------------------------------------------------------------------- 1 | ## Задача 2 | 3 | Напишите программу, которая будет принимать один аргумент командной строки к URL-адресу. 4 | 5 | Используйте `async.whilst` и `http.get`, отправьте GET-запросы на этот URL пока не получите 6 | тело ответа содержащее строку `"meerkat"`. 7 | 8 | `сonsole.log` количества GET-запросов, необходимых для получения "meerkat" строки. 9 | 10 | ## Советы 11 | 12 | `String.prototype.trim()` - это ваш друг. 13 | Узнайте больше о `async.whilst()` здесь: 14 | 15 | https://github.com/caolan/async#whilst 16 | -------------------------------------------------------------------------------- /exercises/whilst/problem.uk.md: -------------------------------------------------------------------------------- 1 | ## Завдання 2 | 3 | Напишіть програму, яка буде приймати один аргумент командної строки як URL-адресу. 4 | 5 | Використовуючи `async.whilst` і `http.get`, відправте GET-запит на цей URL поки не отримаєте 6 | тіло відповіді, що містить рядок `"meerkat"`. 7 | 8 | `сonsole.log` кількості GET-запитів, необхідних для отримання "meerkat" рядка. 9 | 10 | ## Поради 11 | 12 | `String.prototype.trim()` - це ваш приятель. 13 | Дізнайтесь більше про `async.whilst()` тут: 14 | 15 | https://github.com/caolan/async#whilst 16 | -------------------------------------------------------------------------------- /exercises/whilst/solution/solution.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const { get } = require("http"); 3 | const async = require("async"); 4 | 5 | let requestBody = ""; 6 | let count = 0; 7 | 8 | async.whilst( 9 | function (cb) { 10 | cb(null, !/meerkat/.test(requestBody.trim())); 11 | }, 12 | 13 | function (done) { 14 | let body = ""; 15 | get(process.argv[2], (res) => { 16 | res 17 | .on("data", (chunk) => { 18 | body += chunk.toString(); 19 | }) 20 | .on("end", () => { 21 | ++count; 22 | requestBody = body; 23 | done(); 24 | }); 25 | }).on("error", done); 26 | }, 27 | 28 | function (err) { 29 | if (err) return console.error(err); 30 | console.log(count); 31 | } 32 | ); 33 | 34 | -------------------------------------------------------------------------------- /i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "async you - learn to use the async package", 3 | "subtitle": "\u001b[23mSelect an exercise and hit \u001b[3mEnter\u001b[23m to begin" 4 | } 5 | -------------------------------------------------------------------------------- /i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "async you - apprenez à utiliser le module async", 3 | "subtitle" : "\u001b[23mSélectionnez un exercice et tapez \u001b[3mEnter\u001b[23m pour démarrer", 4 | "exercise": { 5 | "WATERFALL": "waterfall", 6 | "SERIES OBJECT": "series avec objet", 7 | "EACH": "each", 8 | "MAP": "map", 9 | "TIMES": "times", 10 | "REDUCE": "reduce", 11 | "WHILST": "whilst" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /i18n/help/en.txt: -------------------------------------------------------------------------------- 1 | {yellow}{bold}Having trouble with a {appname} exercise?{/bold}{/yellow} 2 | 3 | If you're looking for general help with Node.js, the #Node.js 4 | channel on Freenode IRC is usually a great place to find someone 5 | willing to help. There is also the Node.js Google Group: 6 | https://groups.google.com/forum/#!forum/nodejs 7 | 8 | {yellow}{bold}Found a bug with {appname} or just want to contribute?{/bold}{/yellow} 9 | 10 | The official repository for {appname} is: 11 | https://github.com/bulkan/async-you/ 12 | Feel free to file a bug report or (preferably) a pull request. 13 | -------------------------------------------------------------------------------- /i18n/help/fr.txt: -------------------------------------------------------------------------------- 1 | {yellow}{bold}Un souci avec un exercice {appname} ?{/bold}{/yellow} 2 | 3 | Si vous cherchez de l’aide générale sur Node.js, le canal Freenode IRC 4 | #Node.js est souvent un super endroit ou commencer à chercher quelqu’un 5 | qui voudra bien vous aider. Il y a aussi le Google Group Node.js : 6 | 7 | https://groups.google.com/forum/#!forum/nodejs 8 | 9 | {yellow}{bold}Vous avez trouvé un bogue dans {appname} ou voulez simplement contribuer ?{/bold}{/yellow} 10 | 11 | Le dépôt officel de {appname} est ici : 12 | 13 | https://github.com/bulkan/async-you/ 14 | 15 | N’hésitez pas à signaler un bogue ou (encore mieux) à proposer une {italic}pull request{/italic}. 16 | -------------------------------------------------------------------------------- /i18n/help/ja.txt: -------------------------------------------------------------------------------- 1 | {yellow}{bold}{appname}の演習でトラブルが起きましたか?{/bold}{/yellow} 2 | 3 | Node.jsの一般的なヘルプを探しているなら、Freenode IRCの 4 | #Node.jsチャネルで誰かが助けてくれるでしょう。 5 | Node.jsのGoogle Groupもあります。 6 | 7 | https://groups.google.com/forum/#!forum/nodejs 8 | 9 | {yellow}{bold}{appname}のバグを見つけたり、貢献したい時には?{/bold}{/yellow} 10 | 11 | {appname}のオフィシャルリポジトリはこちらです: 12 | https://github.com/bulkan/async-you/ 13 | バグレポートや、改善のプルリクエストはお気軽にどうぞ。 14 | -------------------------------------------------------------------------------- /i18n/help/ru.txt: -------------------------------------------------------------------------------- 1 | {yellow}{bold}Есть проблемы с приложением {appname}?{/bold}{/yellow} 2 | 3 | Если ты ищешь помощь с Node.js, канал #Node.js 4 | на Freenode IRC - это отличное место для поиска желающих помочь. 5 | Здесь также есть Node.js Google Group: 6 | https://groups.google.com/forum/#!forum/nodejs 7 | 8 | {yellow}{bold}Нашел баг в приложении {appname} или просто хочешь помочь в развитии проекта?{/bold}{/yellow} 9 | 10 | Официальный репозиторий для приложения {appname}: 11 | https://github.com/bulkan/async-you/ 12 | Не стесьняйтесь отправить отчет об ошибке или (предпочтительно) пулл-реквест. 13 | -------------------------------------------------------------------------------- /i18n/help/uk.txt: -------------------------------------------------------------------------------- 1 | {yellow}{bold}Існують проблеми з додатком {appname}?{/bold}{/yellow} 2 | 3 | Якщо ти шукаєш допомогу з Node.js, канал #Node.js 4 | на Freenode IRC - це чудове місце для пошуку охочих допомогти. 5 | Також тут є Node.js Google Group: 6 | https://groups.google.com/forum/#!forum/nodejs 7 | 8 | {yellow}{bold}Знайшов баг в додатку {appname} чи просто хочеш допомогти в розвитку проекту?{/bold}{/yellow} 9 | 10 | Офіційний репозиторій для додатку {appname}: 11 | https://github.com/bulkan/async-you/ 12 | Не соромтесь відправити звіт про помилку або (бажано) пул-реквест. 13 | -------------------------------------------------------------------------------- /i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "async you - learn to use the async package", 3 | "subtitle": "\u001b[23mSelect an exercise and hit \u001b[3mEnter\u001b[23m to begin", 4 | "exercise": { 5 | "WATERFALL": "WATERFALL", 6 | "SERIES OBJECT": "SERIES OBJECT", 7 | "EACH": "EACH", 8 | "MAP": "MAP", 9 | "TIMES": "TIMES", 10 | "REDUCE": "REDUCE", 11 | "WHILST": "WHILST" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "async you - изучите как использовать пакет async", 3 | "subtitle": "\u001b[23mВыберите упражнение и нажмите клавишу \u001b[3mEnter\u001b[23m для начала", 4 | "exercise": { 5 | "WATERFALL": "waterfall", 6 | "SERIES OBJECT": "series object", 7 | "EACH": "each", 8 | "MAP": "map", 9 | "TIMES": "times", 10 | "REDUCE": "reduce", 11 | "WHILST": "whilst" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /i18n/uk.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "async you - навчіться використовувати пакет async", 3 | "subtitle": "\u001b[23mВиберіть вправу і натисніть клавішу \u001b[3mEnter\u001b[23m для початку", 4 | "exercise": { 5 | "WATERFALL": "waterfall", 6 | "SERIES OBJECT": "series object", 7 | "EACH": "each", 8 | "MAP": "map", 9 | "TIMES": "times", 10 | "REDUCE": "reduce", 11 | "WHILST": "whilst" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async-you", 3 | "version": "2.0.0", 4 | "description": "Learn async via a set of self-guided workshops.", 5 | "author": "Bulkan Evcimen ", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/bulkan/async-you.git" 9 | }, 10 | "license": "MIT", 11 | "dependencies": { 12 | "async": "^3.2.0", 13 | "concat-stream": "^2.0.0", 14 | "workshopper-adventure": "^6.0.2", 15 | "workshopper-exercise": "^2.3.0" 16 | }, 17 | "engines": { 18 | "node": ">=10.0.0" 19 | }, 20 | "bin": "./async_you.js", 21 | "preferGlobal": true 22 | } 23 | --------------------------------------------------------------------------------