from callback hell

to async heaven

Kristofer Selbekk

BEKK Consulting

JavaScript er synkront

En tråd

Førsteordens funksjoner

Superkrefter!

() => {}

Callbacks
Promises
async / await

Kodetid!

function ajax(url, dataReceived) {
  const request = new XMLHttpRequest();
  request.onreadystatechange = function() {
    if (request.readyState === XMLHttpRequest.DONE && request.status === 200) {
      dataReceived(request.responseText);
    }
  };
  request.open('GET', url);
  request.send();
};

Møt callbacks!

ajax('/api/user', function(user) { 
  console.log(user);
});
ajax('/api/user', function(user) { 
  ajax(`/api/user/${user.id}/profile`, function(profile) {
    ajax(`/api/posts/${profile.preferenceId}`, function (posts) {
      console.log('Preferred posts for user:', posts);
    });
  });
});
ajax('/api/user', function(user) { 
  ajax(`/api/user/${user.id}/profile`, function(profile) {
    ajax(`/api/posts/${profile.preferenceId}`, function (posts) {
      console.log('Preferred posts for user:', posts);
    }, function(err) {
      console.error('Could not load preferences');
    });
  }, function(err) {
    console.error('Could not load profile information');
  });
}, function(err) {
  console.error('Could not load user data');
});
ajax('/api/user', function(user) { 
  if (user.loggedIn) {
    ajax(`/api/user/${user.id}/profile`, function(profile) {
      if (profile.preferenceId) {
        ajax(`/api/posts/${profile.preferenceId}`, function (posts) {
          if (posts.length) {
            console.log('Preferred posts for user:', posts);
          } else {
            console.error('No posts for user');
          }
        }, function(err) {
          console.error('Could not load preferences');
        });
      } else {
        console.error('User does not have any preferences yet');
      }
    }, function(err) {
      console.error('Could not load profile information');
    });
  } else {
    console.log('User is not logged in.');
  }
}, function(err) {
    console.error('Could not load user data');
});

🚫 Lesbarhet
⚠️ Feilhåndtering
⚠️ Scope

Promises

A Promise is an object representing the eventual completion or failure of an asynchronous operation.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

const ajax = (url) => {
  return new Promise((resolve, reject) => {
    oldAjax(url, resolve, reject);
  });
};

Promisify

ajax('/api/user')
  .then(user => console.log(user))
  .catch(err => console.error(err));

Mer lesbart

ajax('/api/user')
  .then(user => ajax(`/api/user/${user.id}/profile`))
  .then(profile => ajax(`/api/posts/${profile.preferenceId}`)
  .then(posts => console.log('Preferred posts for user:', posts);
  .catch(err => console.error(err));

Promise chains

ajax('/api/user')
  .then(user => ajax(`/api/user/${user.id}/profile`))
  .then(profile => ajax(`/api/posts/${profile.preferenceId}`)
  .then(posts => console.log('Preferred posts for user:', posts))
  .catch(err => console.error(err)); // Hvilken error?

Feilhåndtering?

ajax('/api/user')
  .then(user => ajax(`/api/user/${user.id}/profile`))
  .then(profile => ajax(`/api/posts/${profile.preferenceId}`)
  .then(posts => console.log('Preferred posts and user:', posts, user);
  .catch(err => console.error(err));

Scope?

✅ Lesbarhet
⚠️ Feilhåndtering
⚠️ Scope

async

await

🎉

The purpose of async/await functions is to simplify the behavior of using promises synchronously and to perform some behavior on a group of Promises

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

async function fetchUser() {
  const user = await ajax('/api/user');
  console.log(user);
}
const fetchAllTheThings = async () => {
  const user = await ajax('/api/user');
  const profile = await ajax(`/api/user/${user.id}/profile`);
  const posts = await ajax(`/api/posts/${profile.preferenceId}`);
  console.log('Preferred posts for user:', posts);
}

Normal syntaks

const fetchAllTheThings = async () => {
  try {
    const user = await ajax('/api/user');
    const profile = await ajax(`/api/user/${user.id}/profile`);
    const posts = await ajax(`/api/posts/${profile.preferenceId}`);
    console.log('Preferred posts for user:', posts);
  } catch (e) {
    console.error('Failed to load preferred posts for user');
  }
}

Normal feilhåndtering

const fetchAllTheThings = async () => {
  try {
    const user = await ajax('/api/user');
    const profile = await ajax(`/api/user/${user.id}/profile`);
    const posts = await ajax(`/api/posts/${profile.preferenceId}`);
    console.log('Preferred posts and user:', posts, user);
  } catch (err) {
    console.error('Failed to load preferred posts for user', err);
  }
}

Normalt scope

Synkrone spørringer?

const getUserData = async () => {
  const [insurances, claims] = await Promise.all([
    ajax('/api/insurances'),
    ajax('/api/claims'),
  ]);
  return { insurances, claims };
};

✅ Lesbarhet
✅ Feilhåndtering
✅ Scope

Klar til bruk!

+ Babel

@selbekk

bekk.no/jobb

Fra callback hell til async heaven

By Kristofer Giltvedt Selbekk

Fra callback hell til async heaven

  • 300