async

Asynchronicity


Motivation

function loadScript(src) {
  // creates a <script> tag and append it to the page
  // this causes the script to load and run when complete
  let script = document.createElement('script');
  script.src = src;
  document.head.append(script);
}
// load and execute the script
loadScript('/my/script.js'); // 5GB script
// this line is evaluated instantly
loadScript('/my/script.js'); // function foo() {...}

foo(); // no such function

Callbacks

function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(script);
  document.head.append(script);
}
loadScript('/my/script.js', () => foo() );

Zweites Script nach erstem laden?


Callbacks in Callbacks

loadScript('/my/script.js', function(script) {
  alert(`${script.src} loaded`);

  loadScript('/my/script2.js', function(script) {
    alert(`${script.src} loaded`);
  });
});
loadScript('/my/script.js', function(script) {

  loadScript('/my/script2.js', function(script) {

    loadScript('/my/script3.js', function(script) {
      // everything loaded
    });
  });
});

Error Handling

function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;
  
  script.onload = () => callback(null, script);
  script.onerror = () => callback(new Error(`Error loading ${src}`));
  
  document.head.append(script);
}
loadScript('/my/script.js', function(error, script) {
  if (error) {
    // handle error
  } else {
    // script loaded
  }
});

Pyramid of Doom

loadScript('1.js', function(error, script) {

  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('2.js', function(error, script) {
      if (error) {
        handleError(error);
      } else {
        // ...
        loadScript('3.js', function(error, script) {
          if (error) {
                        handleError(error);
          } else {
                        // everything loaded
          }
        });
      }
    });
  }
});

Promises

let promise = new Promise(function(resolve, reject) {
  let success = doStuff(); // starts instantly, takes time
  if (success)
    resolve('done');
  else 
    reject(new Error());
});


let promise = new Promise(function(resolve, reject) {

  // after 1s resolve to done
  setTimeout(() => resolve('done'), 1000);
});


let promise = new Promise(function(resolve, reject) {

  // after 1s someting went wrong
  setTimeout(() => reject(new Error()), 1000);
});


then

promise.then(
  function(result) { /* handle successful result */ },
  function(error) { /* handle error */ }
);
let promise = new Promise(function(resolve, reject) {
  if (Math.random() < .5)
    setTimeout(() => resolve('done'), 1000);
  else
    setTimeout(() => reject(new Error('Whoops!')), 1000);
});

promise.then(
  result => console.log(result), 
  error => console.error(error)
);

catch

let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('Whoops!')), 1000);
  // throw new Error('Whoops!');
});

// promise.then(null, f)
promise.catch(console.info);

finally

try { }
catch { }
finally { }
new Promise((resolve, reject) => {
  this.isLoading.set(true); // start loading animation
  if (Math.random() < .5)
    setTimeout(() => resolve('done'), 1000);
  else
    setTimeout(() => reject(new Error('Whoops!')), 1000);
})
.finally(() => this.isLoading.set(false)) // return as is
.then( ... );

Initial example

function loadScript(src) {
  return new Promise(function(resolve, reject) {
    let script = document.createElement('script');
    script.src = src;

    script.onload = () => resolve(script);
    script.onerror = () => reject(new Error(`Error loading ${src}`));

    document.head.append(script);
  });
}
let promise = loadScript('/my/script.js');

promise.then(
  script => console.info(`${script.src} is loaded!`),
  error => console.error(`Error: ${error.message}`)
);
promise.then(script => console.log('Great success'));

Chaining

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); 
}).then(function(result) {
  console.log(result); // 1
  return result * 2;
}).then(function(result) {
  console.log(result); // 2
  return result * 2;
}).then(function(result) {
  console.log(result); // 4
  return result * 2;  
});


let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000);
});

promise.then(function(result) {
  console.log(result); // 1
  return result * 2;
});

promise.then(function(result) {
  console.log(result); // 1
  return result * 2;
});

promise.then(function(result) {
  console.log(result); // 1
  return result * 2;
});


async/await

async function f() {
  return 42;
  // return Promise.resolve(42);
}

asyncPromise

f().then(console.log); // 42

await

function f() {
  await 42; // 🚫 Syntax Error
}
async function f() {
  await 42; // 👍
}
async function f() {

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

  let result = await promise; // suspends execution
  console.log(result); 
}

f();

thenawait

promise.then(
  result => doStuff(result), 
  error => handle(error)
);
try {
  let result = await promise;
  doStuff(result);
} catch(error) {
  handle(error);
}

Initial example

async function loadScript(src) {
  return new Promise(function(resolve, reject) {
    let script = document.createElement('script');
    script.src = src;

    script.onload = () => resolve(script);
    script.onerror = () => reject(new Error(`Error loading ${src}`));

    document.head.append(script);
  });
}
async function followup() {
  let promise = loadScript('/my/script.js');
  try {
    let script = await promise;
    console.info(`${script.src} is loaded!`);
    console.log('Great success');
  } catch(error) {
    console.error(`Error: ${error.message}`);
  }
}