async

The ES2017 standard introduces async functions to make asynchronous operations more convenient.

In asynchronous processing, async functions are the syntactic sugar of Generator functions.

Here’s an example:

/ / use the generator
var fetch = require('node-fetch');
var co = require('co');

function* gen() {
    var r1 = yield fetch('https://api.github.com/users/github');
    var json1 = yield r1.json();
    console.log(json1.bio);
}

co(gen);
Copy the code

When you use async:

/ / use the async
var fetch = require('node-fetch');

var fetchData = async function () {
    var r1 = await fetch('https://api.github.com/users/github');
    var json1 = await r1.json();
    console.log(json1.bio);
};

fetchData();
Copy the code

In fact, async functions are implemented by wrapping Generator functions and automatic actuators in one function.

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

/ / is equivalent to

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

The spawn function refers to the auto-executor, such as CO.

Combined with the fact that async functions return a Promise object, you can also think of async functions as a wrapper based on Promise and Generator.

Async and Promise

Strictly speaking, async is a syntax, Promise is a built-in object, the two are not comparable, and async also returns a Promise object…

The main point here is to show some scenarios where async is a more elegant way to handle asynchronous processes than Promise.

1. Simpler code

/** */
function fetch() {
  return (
    fetchData()
    .then((a)= > {
      return "done"}); }async function fetch() {
  await fetchData()
  return "done"
};
Copy the code
/** * example 2 */
function fetch() {
  return fetchData()
  .then(data= > {
    if (data.moreData) {
        return fetchAnotherData(data)
        .then(moreData= > {
          return moreData
        })
    } else {
      return data
    }
  });
}

async function fetch() {
  const data = await fetchData()
  if (data.moreData) {
    const moreData = await fetchAnotherData(data);
    return moreData
  } else {
    return data
  }
};
Copy the code
/** * example 3 */
function fetch() {
  return (
    fetchData()
    .then(value1= > {
      return fetchMoreData(value1)
    })
    .then(value2= > {
      return fetchMoreData2(value2)
    })
  )
}

async function fetch() {
  const value1 = await fetchData()
  const value2 = await fetchMoreData(value1)
  return fetchMoreData2(value2)
};
Copy the code

2. Error handling

function fetch() {
  try {
    fetchData()
      .then(result= > {
        const data = JSON.parse(result)
      })
      .catch((err) = > {
        console.log(err)
      })
  } catch (err) {
    console.log(err)
  }
}
Copy the code

In this code, try/catch catches some Promise construction errors in fetchData(), but not the exceptions thrown by json.parse. To handle the exceptions thrown by json.parse, You need to add a catch function to repeat the exception handling logic.

In a real project, the error-handling logic could be complex, leading to redundant code.

async function fetch() {
  try {
    const data = JSON.parse(await fetchData())
  } catch (err) {
    console.log(err)
  }
};
Copy the code

The advent of async/await allows try/catch to catch synchronous and asynchronous errors.

3. The debugging

const fetchData = (a)= > new Promise((resolve) = > setTimeout(resolve, 1000.1))
const fetchMoreData = (value) = > new Promise((resolve) = > setTimeout(resolve, 1000, value + 1))
const fetchMoreData2 = (value) = > new Promise((resolve) = > setTimeout(resolve, 1000, value + 2))

function fetch() {
  return (
    fetchData()
    .then((value1) = > {
      console.log(value1)
      return fetchMoreData(value1)
    })
    .then(value2= > {
      return fetchMoreData2(value2)
    })
  )
}

const res = fetch();
console.log(res);
Copy the code

Because the code in THEN is executed asynchronously, when you break the point, the code is not executed sequentially, especially if you use step over, the then function goes directly to the next THEN function.

const fetchData = (a)= > new Promise((resolve) = > setTimeout(resolve, 1000.1))
const fetchMoreData = (a)= > new Promise((resolve) = > setTimeout(resolve, 1000.2))
const fetchMoreData2 = (a)= > new Promise((resolve) = > setTimeout(resolve, 1000.3))

async function fetch() {
  const value1 = await fetchData()
  const value2 = await fetchMoreData(value1)
  return fetchMoreData2(value2)
};

const res = fetch();
console.log(res);
Copy the code

With async, you can debug it just like synchronous code.

Async hell

Async hell is where the developer takes advantage of syntactic brevity to make things run sequentially instead of in parallel, affecting performance, but hell is a bit of an exaggeration…

Example a

Here’s an example:

(async() = > {const getList = await getList();
  const getAnotherList = awaitgetAnotherList(); }) ();Copy the code

GetList () and getAnotherList() do not depend on each other, but the current notation, while concise, results in getAnotherList() being executed only after getList() returns, doubling the request time.

To solve this problem, we can change it to this:

(async() = > {const listPromise = getList();
  const anotherListPromise = getAnotherList();
  await listPromise;
  awaitanotherListPromise; }) ();Copy the code

Promise.all() can also be used:

(async() = > {Promise.all([getList(), getAnotherList()]).then(...) ; }) ();Copy the code

Example 2

Of course, the above example is relatively simple, so let’s expand it:

(async() = > {const listPromise = await getList();
  const anotherListPromise = await getAnotherList();

  // do something

  await submit(listData);
  awaitsubmit(anotherListData); }) ();Copy the code

GetList () and getAnotherList() have no dependencies. Submit (listData) and submit(anotherListData) have no dependencies. So how do we rewrite this case?

Basically, there are three steps:

1. Identify dependencies

Here, submit(listData) needs to come after getList() and submit(anotherListData) needs to come after anotherListPromise().

2. Wrap interdependent statements in async functions

async function handleList() {
  const listPromise = await getList();
  // ...
  await submit(listData);
}

async function handleAnotherList() {
  const anotherListPromise = await getAnotherList()
  // ...
  await submit(anotherListData)
}
Copy the code

3. Execute async functions concurrently

async function handleList() {
  const listPromise = await getList();
  // ...
  await submit(listData);
}

async function handleAnotherList() {
  const anotherListPromise = await getAnotherList()
  // ...
  await submit(anotherListData)
}

/ / method
(async() = > {const handleListPromise = handleList()
  const handleAnotherListPromise = handleAnotherList()
  await handleListPromise
  await handleAnotherListPromise
})()

/ / method 2
(async() = > {Promise.all([handleList(), handleAnotherList()]).then()
})()
Copy the code

Secondary and concurrent

Question: Given an array of urls, how to implement secondary and concurrent interfaces?

Async secondary implementation:

/ / a secondary one
async function loadData() {
  var res1 = await fetch(url1);
  var res2 = await fetch(url2);
  var res3 = await fetch(url3);
  return "whew all done";
}
Copy the code
/ / secondary two
async function loadData(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(awaitresponse.text()); }}Copy the code

Async concurrency implementation:

/ / concurrency
async function loadData() {
  var res = await Promise.all([fetch(url1), fetch(url2), fetch(url3)]);
  return "whew all done";
}
Copy the code
/ / concurrent ii
async function loadData(urls) {
  // Read the URL concurrently
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text();
  });

  // Output in order
  for (const textPromise of textPromises) {
    console.log(awaittextPromise); }}Copy the code

Async error catching

Although we can use try catches to catch errors, try catches can quickly lead to code clutter when we need to catch multiple errors and do different things, as in:

async function asyncTask(cb) {
    try {
       const user = await UserModel.findById(1);
       if(! user)return cb('No user found');
    } catch(e) {
        return cb('Unexpected error occurred');
    }

    try {
       const savedTask = await TaskModel({userId: user.id, name: 'Demo Task'});
    } catch(e) {
        return cb('Error occurred while saving task');
    }

    if(user.notificationsEnabled) {
        try {
            await NotificationService.sendNotification(user.id, 'Task Created');
        } catch(e) {
            return cb('Error while sending notification'); }}if(savedTask.assignedUser.id ! == user.id) {try {
            await NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you');
        } catch(e) {
            return cb('Error while sending notification');
        }
    }

    cb(null, savedTask);
}
Copy the code

To simplify error catching, we can add a catch function to the await promise object. To do this, we need to write a helper:

// to.js
export default function to(promise) {
   return promise.then(data= > {
      return [null, data];
   })
   .catch(err= > [err]);
}
Copy the code

The entire error capture code can be simplified as:

import to from './to.js';

async function asyncTask() {
     let err, user, savedTask;

     [err, user] = await to(UserModel.findById(1));
     if(! user)throw new CustomerError('No user found');

     [err, savedTask] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
     if(err) throw new CustomError('Error occurred while saving task');

    if(user.notificationsEnabled) {
       const [err] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
       if (err) console.error('Just log the error and continue flow'); }}Copy the code

Some discussion of Async

Will Async replace Generator?

Using a Generator to handle asynchronous requests is just a hack. Async can replace Generator in the asynchronous context, but async and Generator syntax are themselves designed to solve different problems.

Will Async replace Promise?

  1. The async function returns a Promise object

  2. Promise’s all and Race are much more useful for complex asynchronous processes

  3. Promise itself is an object, so it can be passed anywhere in the code

  4. Async still has low approval ratings, and even with Babel, it takes about 1000 lines to compile.

reference

  1. Six aspects of Async/Await superior to Promise
  2. How to escape the hell of async/await
  3. Intensive reading “Async /await is a double-edged sword”
  4. ECMAScript introduction to 6
  5. How to write async await without try-catch blocks in Javascript

ES6 series

ES6 directory address: github.com/mqyqingfeng…

ES6 series is expected to write about 20 chapters, aiming to deepen the understanding of ES6 knowledge points, focusing on the block-level scope, tag template, arrow function, Symbol, Set, Map and Promise simulation implementation, module loading scheme, asynchronous processing and other contents.

If there is any mistake or not precise place, please be sure to give correction, thank you very much. If you like or are inspired by it, welcome star and encourage the author.