ES7 – Async

 · 4 mins read

The async function declaration defines an asynchronous function, which returns an AsyncFunction object.

Syntax

async function name([param[, param[, ... param]]]) {
   statements
}

When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.

An async function can contain an await expression, that pauses the execution of the async function and waits for the passed Promise’s resolution, and then resumes the async function’s execution and returns the resolved value.

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. Just as Promises are similar to structured callbacks, async/await is similar to combining generators and promises.

So what we can get is the purpose of these two keywords:

async/await is similar to combining generators and promises

An API that returns a Promise will result in a promise chain, and it splits the function into many parts.

I don’t think Promise is elegant and reader-friendly enough…

After we got async/awit, times change!

<br />function getProcessedData(url) {
  return downloadData(url) // returns a promise
    .catch(e => {
      return downloadFallbackData(url)  // returns a promise
    })
    .then(v => {
      return processDataInWorker(v); // returns a promise
    });
}

//Rewriting a promise chain with an async function

async function getProcessedData(url) {
  let v;
  try {
    v = await downloadData(url); 
  } catch(e) {
    v = await downloadFallbackData(url);
  }
  return processDataInWorker(v);
}


Note that in the above example, there is no await statement on the return statement, because the return value of an async function is implicitly wrapped in Promise.resolve.

Actually, any async function can be refactor

async function fn(args) {
  // ...
}

// equals to 

function fn(args) {
  return spawn(function* () {
    // ...
  });
}

//spawn is a automatic executor
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

Comparison between Promise, Generator, and Async

Assuming that we have a DOM element, we implement a series of animations. It requires that excute one after the previous one succeed, and if something wrong happened, return the value of last successful one.

1.Promise

function chainAnimationsPromise(elem, animations) {

  //variable ret store the returned value of last one
  let ret = null;

  // create a empty Promise object
  let p = Promise.resolve();

  // using then to get all returned values of animations 
  for(let anim of animations) {
    p = p.then(function(val) {
      ret = val;
      return anim(elem);
    });
  }

  // return a Promise which has already bound the error handler
  return p.catch(function(e) {
    //just ignore, then continue to excute
  }).then(function() {
    return ret;
  });

}

As you can see, using Promise has done relatively well in processing asynchronous tasks in comparison with traditional callbacks, but the whole code segment is full of APIs of Promise, that maybe confuse people a little bit especially when they are not familiar with Promise API.

2.Generator

function chainAnimationsGenerator(elem, animations) {

  return spawn(function*() {
    let ret = null;
    try {
      for(let anim of animations) {
        ret = yield anim(elem);
      }
    } catch(e) {
      //just ignore, then continue to excute
    }
    return ret;
  });

}

It’s much better than Promise when looking from the structure of code segment, no callbacks, no other APIs. But you need the automatic executor like spawn in the above context.

3.Async

async function chainAnimationsAsync(elem, animations) {
  let ret = null;
  try {
    for(let anim of animations) {
      ret = await anim(elem);
    }
  } catch(e) {
    //just ignore, then continue to excute
  }
  return ret;
}

Simple!

Sync-like!

Awesome!

That’s what I think the most perfect solution is so far.

Advanced async

  1. Async Iterator
  2. for await…of
  3. Async Generartor

Here is just a syllabus. I want to talk about it later.


LINKS:

async 函数

async – MDN