为什么需要异步?

因为基于JavaScript的语言自身的特点: Javascript语言的执行环境是单线程。即一次只能完成一个任务。若有多个任务则需排队逐个执行——前一个任务完成,再执行后一个任务。

为避免和解决这种问题,JS语言将任务执行模式分为异步和同步。同步模式”就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;”异步模式”则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

前端常见的异步操作:

1、回调函数

也就是函数层层嵌套,参考自己文章:搞定callback-hell(回调地狱)、promise 回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调。

2、Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

两个特点:

  • 1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变
  • 2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的

promise对象简单例子:

  1. function timeOut(ms) {
  2. const p = new Promise((resolve, reject) => {
  3. setTimeout(resolve, ms, "done");
  4. });
  5. return p;
  6. }
  7. timeOut(2000).then(value => {
  8. console.log(value); //两秒后打印 deon
  9. });

timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(ms参数)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。

⚠️: promise新建后会立即执行

  1. let promise = new Promise(function(resolve, reject) {
  2. console.log('Promise');
  3. resolve();
  4. });
  5. promise.then(function() {
  6. console.log('resolved.');
  7. });
  8. console.log('Hi!');
  9. // Promise
  10. // Hi!
  11. // resolved

promise异步加载图片的例子

  1. function loadImg(url) {
  2. const p = new Promise((resolve, reject) => {
  3. const img = new Image();
  4. img.onload = () => {
  5. resolve(img);
  6. console.log(img.width); //1280
  7. };
  8. img.onerror = () => {
  9. reject(new Error("Could not load image at " + url));
  10. };
  11. img.src = url;
  12. });
  13. return p;
  14. }
  15. const url1 =
  16. "https://user-gold-cdn.xitu.io/2020/3/27/1711ae8edcd5b389?imageView2/0/w/1280/h/960/format/webp/ignore-error/1";
  17. console.log(loadImg(url1)); //Promise {<pending>}

上面代码中,使用Promise包装了一个图片加载的异步操作。如果加载成功,就调用resolve方法,否则就调用reject方法。

promise对象实现Ajax:

  1. // promise 实现ajax请求
  2. function getAjax(url) {
  3. const p = new Promise((resolve, reject) => {
  4. const handler = function() {
  5. if (this.readyState !== "4") {
  6. return;
  7. }
  8. if (this.status === 200) {
  9. resolve(this.response);
  10. } else {
  11. reject(new Error(this.statusText));
  12. }
  13. };
  14. const client = new XMLHttpRequest();
  15. client.open("GET", url);
  16. client.onreadystatechange = handler;
  17. client.responseType = "json";
  18. client.setRequestHeader("Accept", "application/json");
  19. client.send();
  20. });
  21. return p;
  22. }
  23. getAjax("/posts.json").then(
  24. function(json) {
  25. console.log("Contents: " + json);
  26. },
  27. function(error) {
  28. console.error("出错了", error);
  29. }
  30. );

readState.png

onreadystatechange.png

3、async/await

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 函数是什么?一句话,它就是 Generator 函数的语法糖。 async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已