Here's a deeper dive into these drills with answers, incorporating callbacks and promises:
Goal: Given an array of integers, write a function to move all zeros to the end without changing the order of non-zero elements.
function moveZeros(arr) { const nonZeros = arr.filter(num => num !== 0); const zeros = new Array(arr.length - nonZeros.length).fill(0); return nonZeros.concat(zeros); }
String Reversal without Built-in Functions
Goal: Write a function that takes a string and returns it reversed. { let reversed = ''; for(let char of str) { reversed = char + reversed; } return reversed; } Goal: Implement a factorial function using recursion and pass the result to a callback.
Palindrome Checker with Callback
Goal: Create a function that checks if a given word is a palindrome. Fibonacci Sequence with Promise Goal: Generate the Fibonacci sequence up to the n-th term using recursion. function fibonacci(n) {
return new Promise((resolve, reject) => {
if (n < 0) reject("Invalid input");
if (n <= 1) return resolve(n);
Promise.all([fibonacci(n-1), fibonacci(n-2)])
.then(([a, b]) => resolve(a + b))
.catch(reject);
});
}
fibonacci(5).then(console.log); // 5
When n is less than or equal to 1, we can instantly resolve the promise with n. For values of n greater than 1, we use Promise.all to wait for both recursive promises (fibonacci(n-1) and fibonacci(n-2)) to resolve. Once both promises have resolved, we sum their results and then resolve the current promise with that sum. This will give the correct result for fibonacci(5). However, note that this approach, while showcasing the combination of promises and recursion, is inefficient for larger values of n due to the exponential growth in the number of recursive calls. A more efficient solution would be to use an iterative approach or memoization with the recursive approach.
Let's break down the code line by line, focusing particularly on the operation of resolve and reject.
javascriptCopy code
function fibonacci(n) {
Here, we declare a function named fibonacci that takes a single argument, n, which represents the term number for which we want to find the Fibonacci value.
javascriptCopy code
return new Promise((resolve, reject) => {
We're returning a new Promise object. A promise in JavaScript represents a value which might be available now, or in the future, or never. The promise constructor takes one argument: a function (often called the "executor function") with two parameters, resolve and reject, which are both functions.
The resolve function is used to signal the successful completion of the promise. When you call resolve, the promise's status changes from "pending" to "fulfilled", and any .then chained to the promise will execute its callback. The reject function is used to signal that the promise has failed for some reason. When you call reject, the promise's status changes from "pending" to "rejected", and any .catch chained to the promise will execute its callback. javascriptCopy code
if (n < 0) reject("Invalid input");
This line checks if n is negative. If it is, the promise is immediately rejected with the reason "Invalid input". This means that the promise has failed and won't provide a valid Fibonacci number.
javascriptCopy code
if (n <= 1) return resolve(n);
This is a base case for our recursive function. If n is 0 or 1, the Fibonacci value is equal to n. So, we immediately resolve the promise with n and return. This stops further execution within the executor function.
javascriptCopy code
Promise.all([fibonacci(n-1), fibonacci(n-2)])
For values of n greater than 1, we're calculating the Fibonacci sequence using recursion. Here, we're making two recursive calls to fibonacci(n-1) and fibonacci(n-2), each of which returns a promise. Promise.all is a method that takes an array of promises and returns a new promise that only resolves when all of the input promises have resolved. If any of the input promises reject, the output promise is immediately rejected.
javascriptCopy code
.then(([a, b]) => resolve(a + b))
If both promises (from the recursive calls) successfully resolve, their results are available as an array in the .then method. We destructure the results into a and b and then resolve our main promise with their sum, which is the Fibonacci value for term n.
javascriptCopy code
.catch(reject);
If either of the recursive calls results in a rejected promise (which, in this specific scenario, shouldn't happen unless n is negative), we directly reject our main promise with the reason provided by the failed promise.
javascriptCopy code
});
}
This closes our executor function and the fibonacci function definition.
javascriptCopy code
fibonacci(5).then(console.log); // 5
Lastly, we're calling the fibonacci function with an argument of 5 and attaching a .then method to log the result. If the promise resolves successfully, we'll log the resolved value (in this case, the Fibonacci value for term 5).
Goal: Write a function that takes in a multi-dimensional array and returns a flattened version of it. function flatten(arr) { return arr.reduce((flat, item) => { return flat.concat(Array.isArray(item) ? flatten(item) : item); }, []); } Goal: Implement a debounce function. function debounce(fn, delay) { let timer; return function(...args) { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); } } const debouncedLog = debounce((text) => console.log(text), 1000); debouncedLog("Test"); Goal: Write a function that squares a number after a delay, using promises. function delayedSquare(n) { return new Promise((resolve) => { setTimeout(() => { resolve(n * n); }, 1000); }); } delayedSquare(5).then(console.log); // 25 Error Handling with Promises Goal: Create a function that returns a promise which rejects after a delay. function failAfterDelay(delay) { return new Promise((resolve, reject) => { setTimeout(() => reject("Failed!"), delay); }); } failAfterDelay(1000).catch(console.error); // "Failed!" Fetching Data using Promises Goal: Fetch data from an API using promises. javascriptCopy code
function fetchData(url) {
return fetch(url).then(response => {
if (!response.ok) throw new Error("Network error");
return response.json();
});
}
fetchData("https://api.example.com/data").then(console.log).catch(console.error);
I hope these drills help you practice JavaScript, callbacks, and promises effectively!