Home >Web Front-end >JS Tutorial >Detailed introduction to Async functions in ES6 (with examples)

Detailed introduction to Async functions in ES6 (with examples)

不言
不言forward
2018-10-24 10:33:492719browse

This article brings you a detailed introduction to the Async function in ES6 (with examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

async

The ES2017 standard introduces the async function, making asynchronous operations more convenient.

In terms of asynchronous processing, the async function is the syntactic sugar of the Generator function.

For example:

// 使用 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);

When you use async:

// 使用 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();

In fact, the implementation principle of the async function is to wrap the Generator function and the automatic executor in a function inside.

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

// 等同于

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

The spawn function refers to the automatic executor, such as co.

In addition, the async function returns a Promise object, you can also understand that the async function is a layer of encapsulation based on Promise and Generator.

async and Promise

Strictly speaking, async is a syntax and Promise is a built-in object. The two are not comparable, not to mention that the async function also returns a Promise object...

Here is mainly to show some scenarios. Using async will handle asynchronous processes more elegantly than using Promise.

1. The code is more concise

/**
 * 示例一
 */
function fetch() {
  return (
    fetchData()
    .then(() => {
      return "done"
    });
  )
}

async function fetch() {
  await fetchData()
  return "done"
};
/**
 * 示例二
 */
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
  }
};
/**
 * 示例三
 */
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)
};

2. Error handling

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

In this code, try/catch can capture some Promise construction errors in fetchData(), However, exceptions thrown by JSON.parse cannot be caught. If you want to handle exceptions thrown by JSON.parse, you need to add a catch function to repeat the exception handling logic.

In actual projects, error handling logic may be complex, which can lead to redundant code.

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

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

3. Debugging

const fetchData = () => 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);

Detailed introduction to Async functions in ES6 (with examples)

Because the code in then is executed asynchronously, when you interrupt the point, the code It will not be executed sequentially, especially when you use step over, the then function will directly enter the next then function.

const fetchData = () => new Promise((resolve) => setTimeout(resolve, 1000, 1))
const fetchMoreData = () => new Promise((resolve) => setTimeout(resolve, 1000, 2))
const fetchMoreData2 = () => 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);

Detailed introduction to Async functions in ES6 (with examples)

When using async, you can debug it just like debugging synchronous code.

async hell

async hell mainly refers to the fact that developers are greedy for grammatical simplicity and turn content that can be executed in parallel into sequential execution, thus affecting performance, but using hell to describe it is a bit A bit exaggerated...

Example 1

For example:

(async () => {
  const getList = await getList();
  const getAnotherList = await getAnotherList();
})();

getList() and getAnotherList() actually have no dependency, but the current way of writing, Although simple, it causes getAnotherList() to be executed only after getList() returns, thus doubling the request time.

In order to solve this problem, we can change it to this:

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

You can also use Promise.all():

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

Example 2

Of course the above This example is relatively simple, let's expand it again:

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

  // do something

  await submit(listData);
  await submit(anotherListData);

})();

Because of the characteristics of await, the entire example has an obvious sequence. However, getList() and getAnotherList() actually have no dependencies. submit(listData) and submit( anotherListData) has no dependencies, so how should we rewrite this example?

Basically divided into three steps:

1. Find out the dependencies

Here, submit(listData) needs to be after getList() , submit(anotherListData) needs to be 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)
}

3. Concurrently execute async functions

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

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

// 方法一
(async () => {
  const handleListPromise = handleList()
  const handleAnotherListPromise = handleAnotherList()
  await handleListPromise
  await handleAnotherListPromise
})()

// 方法二
(async () => {
  Promise.all([handleList(), handleAnotherList()]).then()
})()

Succession and concurrency

Question: Given a URL array, how to implement the succession and concurrency of the interface?

async Subsequent implementation:

// 继发一
async function loadData() {
  var res1 = await fetch(url1);
  var res2 = await fetch(url2);
  var res3 = await fetch(url3);
  return "whew all done";
}
// 继发二
async function loadData(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}

async Concurrent implementation:

// 并发一
async function loadData() {
  var res = await Promise.all([fetch(url1), fetch(url2), fetch(url3)]);
  return "whew all done";
}
// 并发二
async function loadData(urls) {
  // 并发读取 url
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text();
  });

  // 按次序输出
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}

async error capture

Although we can use try catch to capture errors, But when we need to catch multiple errors and do different processing, try catch will soon lead to messy code, such as:

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);
}

In order to simplify the capture of this kind of error, we can give promise after await Object adds a catch function, for which we need to write a helper:

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

The entire error catching code can be simplified to:

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');
    }
}

Some discussions on async

async will replace Generator ?

Generator is originally used as a generator. Using Generator to handle asynchronous requests is just a hack. In terms of asynchronousness, async can replace Generator, but the two syntaxes of async and Generator are used to solve different problems. of.

Will async replace Promise?

  1. The async function returns a Promise object

  2. In the face of complex asynchronous processes, the all and race provided by Promise will be more useful

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

  4. The support rate of async is still very low. Even with Babel, it needs to be compiled after compilation. Add about 1000 lines.


The above is the detailed content of Detailed introduction to Async functions in ES6 (with examples). For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete