Maison >interface Web >js tutoriel >Introduction détaillée aux fonctions Async dans ES6 (avec exemples)

Introduction détaillée aux fonctions Async dans ES6 (avec exemples)

不言
不言avant
2018-10-24 10:33:492735parcourir

Cet article vous apporte une introduction détaillée à la fonction Async dans ES6 (avec des exemples). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

async

La norme ES2017 introduit la fonction asynchrone, rendant les opérations asynchrones plus pratiques.

En terme de traitement asynchrone, la fonction async est le sucre syntaxique de la fonction Generator.

Par exemple :

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

Lorsque vous utilisez l'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();

En fait, le principe de mise en œuvre de la fonction async est de combiner la fonction Générateur et l'automatique exécuteur. Enveloppé dans une fonction.

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

// 等同于

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

La fonction spawn fait référence à un exécuteur automatique, tel que co.

De plus, la fonction async renvoie un objet Promise, vous pouvez également comprendre que la fonction async est une couche d'encapsulation basée sur Promise et Generator.

async et Promise

À proprement parler, async est une syntaxe et Promise est un objet intégré. Les deux ne sont pas comparables, sans oublier que la fonction async renvoie également un objet Promise. ..

Voici principalement pour montrer quelques scénarios. L'utilisation d'async gérera les processus asynchrones avec plus d'élégance que l'utilisation de Promise.

1. Le code est plus concis

/**
 * 示例一
 */
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. Gestion des erreurs

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

Dans ce code, try/catch peut capturer. Certaines erreurs de construction Promise dans fetchData(), mais les exceptions levées par JSON.parse ne peuvent pas être interceptées. Si vous souhaitez gérer les exceptions levées par JSON.parse, vous devez ajouter une fonction catch pour répéter la logique de gestion des exceptions.

Dans les projets réels, la logique de gestion des erreurs peut être complexe, ce qui peut conduire à du code redondant.

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

L'émergence de async/await permet à try/catch de capturer les erreurs synchrones et asynchrones.

3. Débogage

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

Introduction détaillée aux fonctions Async dans ES6 (avec exemples)

Parce que le code dans then est exécuté de manière asynchrone, lorsque vous interrompez le point, le code ne sera pas exécuté séquentiellement, surtout lorsque vous utilisez step over, la fonction then entrera directement dans la fonction then suivante.

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

Introduction détaillée aux fonctions Async dans ES6 (avec exemples)

Lorsque vous utilisez async, vous pouvez le déboguer tout comme le débogage du code synchrone.

l'enfer asynchrone

l'enfer asynchrone signifie principalement que les développeurs sont avides de simplicité grammaticale et transforment le contenu qui peut être exécuté en parallèle en exécution séquentielle, affectant ainsi les performances, mais utiliser l'enfer pour le décrire est un bit Un peu exagéré...

Exemple 1

Par exemple :

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

getList() et getAnotherList() n'ont en fait aucune dépendance, mais maintenant ceci Bien que le La méthode d'écriture est concise, elle provoque l'exécution de getAnotherList() uniquement après le retour de getList(), doublant ainsi le temps de requête.

Afin de résoudre ce problème, nous pouvons le changer comme suit :

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

Vous pouvez également utiliser Promise.all() :

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

Exemple 2

Bien sûr, l'exemple ci-dessus est relativement simple, développons-le :

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

  // do something

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

})();

En raison des caractéristiques de wait, l'exemple entier a une séquence évidente. Cependant, getList() et. getAnotherList() n'a en fait aucune dépendance, submit (listData) et submit(anotherListData) n'ont également aucune dépendance, alors comment devrions-nous réécrire cet exemple ?

Basiquement divisé en trois étapes :

1 Découvrez les dépendances

Ici, submit(listData) doit être après getList() , submit(anotherListData) doit être après anotherListPromise().

2. Enveloppez les instructions interdépendantes dans des fonctions asynchrones

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

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

3. Exécutez des fonctions asynchrones simultanément

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 et concurrence

Question : Étant donné un tableau d'URL, comment implémenter la succession et la concurrence de l'interface ?

implémentation secondaire asynchrone :

// 继发一
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());
  }
}

implémentation simultanée asynchrone :

// 并发一
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);
  }
}

capture d'erreur asynchrone

Bien que nous puissions utiliser try catch pour détecter les erreurs, lorsque nous devons détecter plusieurs erreurs et effectuer des traitements différents, try catch conduira bientôt à un code encombré, tel que :

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

Afin de simplifier cela Pour capturer erreurs, nous pouvons ajouter une fonction catch à l'objet de promesse après wait. Pour cela, nous devons écrire un assistant :

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

L'intégralité du code de capture d'erreur peut être simplifiée en :

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

Quelques discussions sur l'async

L'async remplacera-t-il Generator ?

Generator est à l'origine utilisé comme générateur. Utiliser Generator pour gérer les requêtes asynchrones n'est qu'un hack. En termes d'asynchronisme, async peut remplacer Generator, mais les deux syntaxes async et Generator sont utilisées pour résoudre des problèmes différents. de.

L'asynchrone remplacera-t-il Promise ?

  1. La fonction asynchrone renvoie un objet Promise

  2. Face à des processus asynchrones complexes, le tout et la race fournis par Promise seront plus utiles

  3. La promesse elle-même est un objet, elle peut donc être passée arbitrairement dans le code

  4. Le taux de prise en charge de l'async est encore très faible. avec Babel, il faut le compiler après compilation. Ajoutez environ 1000 lignes.


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer