Today we will touch on one of the most advance concept of JavaScript : Asynchronous.

By default JavaScript execute synchronously, meaning from line 1 to last line of JS file, top to botom. This is one limitation if we running this scenario :

You get the idea, JS don't wait! But we could tell it to wait. Kind of

Illustration diagram

In programming, we often have a "producing code" that does something and takes time. For instance, code that loads the data over a network. We also have "consuming code" that wants the result of the "producing code" once it's ready. A promise is a special JavaScript object that links the "producing code" and the "consuming code" together.

let promise = new Promise(function (resolve, reject) {
  // executor
  setTimeout(() => resolve("done"), 1000);
});
console.log(promise);
// Promise { <pending> }

The function passed to new Promise is called the executor. When new Promise is created, the executor runs automatically. Its arguments resolve and reject are callbacks provided by JavaScript itself. Our code is only inside the executor. When the executor obtains the result, be it soon or late, doesn't matter, it should call one of these callbacks:

Consumers: then, catch, finally

A Promise object serves as a link between the executor and the consuming functions, which will receive the result or error.

The most important, fundamental one is .then

promise.then(
  function (result) {
    /* handle a successful result */
  },
  function (error) {
    /* handle an error */
  }
);

Example

let promise = new Promise(function (resolve, reject) {
  // executor
  setTimeout(() => resolve("done"), 1000);
});
// resolve runs the first function in .then
promise.then(
  (result) => console.log(result), // "done" after 1 second
  (error) => console.log(error) // doesn't run
);
let promise = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// reject runs the second function in .then
promise.then(
  (result) => console.log(result), // doesn't run
  (error) => console.log(error) // shows "Error: Whoops!" after 1 second
);

If we're interested only in successful completions, then we can provide only one function argument to .then:

promise.then(console.log);

If we're interested only in errors, then we can use null as the first argument: .then(null, errorHandlingFunction). Or we can use .catch(errorHandlingFunction), which is exactly the same:

let promise = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});
promise.catch(console.log); // shows "Error: Whoops!" after 1 second

The call .finally(cb) means that the function cb (callback) always runs when the promise is settled (resolve or reject). finally is good handler for performing cleanup.

There's a special syntax to work with promises in a more comfortable fashion, called "async/await". It's surprisingly easy to understand and use.

Async

The word async before a function means one simple thing: a function always returns a promise. Other values are wrapped in a resolved promise automatically.

async function f() {
  return 1;
}

f().then(alert); // 1

Await

The keyword await makes JavaScript wait until that promise settles and returns its result.

async function f() {
  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("done!"), 1000);
  });

  let result = await promise; // wait until the promise resolves (*)

  console.log(result); // "done!"
}

f();

The function execution "pauses" at the line (*) and resumes when the promise settles, with result becoming its result. So the code above shows "done!" in one second.

Let's emphasize: await literally suspends the function execution until the promise settles, and then resumes it with the promise result. That doesn't cost any CPU resources, because the JavaScript engine can do other jobs in the meantime: execute other scripts, handle events, etc.

It's just a more elegant syntax of getting the promise result than promise.then, easier to read and write.

Note: you can't use await in non-async function, there would be a syntax error

Note: you can't use await in top-level code, you need to wrap it into an async function

Error handling

In the case of a rejection, await promise throws an error. We can catch that error using try..catch:

async function f() {
  try {
    let response = await fetch("/no-user-here");
    let user = await response.json();
  } catch (err) {
    // catches errors both in fetch and response.json
    alert(err);
  }
}

f();

The async keyword before a function has two effects:

The await keyword before a promise makes JavaScript wait until that promise settles, and then:

Together they provide a great framework to write asynchronous code that is easy to both read and write.

With async/await we rarely need to writepromise.then/catch, but we still shouldn't forget that they are based on promises, because sometimes (e.g. in the outermost scope) we have to use these methods. Also Promise.all is nice when we are waiting for many tasks simultaneously.

Google News Clone

Demo

Today we'll clone Google News. We'll use the News API to do so.

This app will show the latest news to the user. This should be a pretty exciting, as it's your first "real" app that could be useful to an end user. Congratulations on making it this far.

What You'll Learn

Required Features

mkdir 3.3-codernews
cd 3.3-codernews
touch index.html style.css script.js

JavaScript can send network requests to the server and load new information whenever it's needed.

There's an umbrella term "AJAX" (abbreviated Asynchronous JavaScript And XML) for network requests from JavaScript. We don't have to use XML though: the term comes from old times, that's why that word is there. You may have heard that term already.

The fetch() method is a way to send a network request and get information from the server.

let promise = fetch(url, [options]);

options is optional parameters: method, headers, etc.. Without options, that is a simple GET request, downloading the contents of the url.

fetch() return a promise which resolves with an object of the built-in Response class as soon as the server responds with headers.

To the the response body, we need to use an additional method call

let response = await fetch(url);
let commits = await response.json(); // read response body and parse as JSON

Or the same without await, using pure promises syntax:

fetch(url')
  .then(response => response.json())
  .then(commits => alert(commits[0].author.login));

POST requests

let user = {
  name: "John",
  email: "john2020@gmail.com",
};

let response = await fetch(url, {
  method: "POST",
  headers: {
    "Content-Type": "application/json;charset=utf-8",
  },
  body: JSON.stringify(user),
});

let result = await response.json();
console.log(result.message);

Fetch options:

We always want new content, whether the user comes today or next year. We'll do so via API.

Make sure to replace the parameter apiKey with the one we got a moment ago below.

const url =
  "https://newsapi.org/v2/top-headlines?country=us&apiKey=___OUR_API_KEY___";

Log the data you get back and make sure it's what we expect.

async function getArticles() {
  const response = await fetch(url);
  const json = await response.json();

  console.log({ json });
}
getArticles();

Negative : If you encounter CORS issues try navigating to http://localhost:YOUR_PORT instead of the default IP opened by live server. http://localhost:5500/ instead of http://127.0.0.1:5500/

Alright we've gotten the data, now we need to inject it to our UI.

async function getArticles() {
  const response = await fetch(url);
  const json = await response.json();
  const { articles } = json;
  document.getElementById("title").innerHTML = `CoderNews (${articles.length})`;
}

Here we pass a callback to map(), renderArticle(). This callback will produce the HTML for a singleArticle

async function getArticles() {
  const response = await fetch(url);
  const json = await response.json();
  const { articles } = json;
  document.getElementById("title").innerHTML = `CoderNews (${articles.length})`;
  const articlesHTML = articles.map(renderArticle);
  document.getElementById("newsList").innerHTML = articlesHTML.join("");
}
function renderArticle(article) {
  return `
    <li class="mb-3 align-self-center article">
        ${article.title}
        <img src="${article.urlToImage}" alt="Snow" />
      </div>
        <i class="fa fa-edit fa-xs"></i><h4 class="mb-0">${article.author}</h4>
        <h6 class="mb-0"><a href="${article.url}">${article.source.name}</a></h6>
      <p><i class="fa fa-envelope"></i>${article.content}</p>
    </li>
  `;
}

Google News Clone

Positive : If you encounter CORS issues you can try this link's suggestions.

Required Features

Grade

Description

F

Some requirements done and code on Github.

C

All requirements done and code on Github.

A

All requirements done and code on Github. App beautiful and works well on mobile