Javascript语言的执行环境是"单线程"(single thread,就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推)。

  Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

  本文则是主要讲解异步的主要几种实现方式。

回调模式

  那么什么是回调呢?

  答:将函数B作为参数传给函数A时,B即是回调函数。

  回调全都是异步吗?

  答:根据回调的定义可以知道,并非所有的回调都是异步。

    var array = [10, 20, 30, 40];
    var b = function (item) {
        if (item) {
            console.log(item);
        } else {
            console.log("No Parameter");
        }
    };
    //非异步
    array.map(b);
    console.log("Sync");
    //异步
    setTimeout(b, 100);
    console.log("async");

1576465575246.jpg 根据如上代码及执行结果可知道代码的执行顺序与定义顺序不一定一致,且并非所有的回调都是异步。 再看下一段代码:

    var button = document.getElementById("button");
    var successCallback = function () {
        console.log("success");
    }
    button.onclick(function (event) {
        var ajax = new XMLHttpRequest();
        ajax.open('get', 'https://www.baidu.com');
        ajax.onreadystatechange = function (event) {
            if (ajax.readyState === 4 && ajax.status === 200) {
                setTimeout(function () {
                    successCallback();
                }, 500)
            }
        };
        ajax.send();
    })

当我们看这段代码时,我们的脑子里会闪过各种各样的回调,未必能很快就知道代码的含义。当回调的层次越来越多,我们将会陷入回调的死亡循环中。这也被称为回调地狱。

Promise

  promise主要用于异步计算、网络请求等。promise可以将异步操作队列化、按照期望的顺序执行并返回预期的结果。

  promise是一个对象,对象与函数最大的区别就是对象可以保存状态,而函数(不包括闭包)不可以;它并未剥夺函数return的能力,因此不需要层层传递callback以回调获取数据。

    new Promise((resolve, reject) => {
        //一些异步处理操作代码
        resolve(res);//异步操作处理成功时,fulfilled
        reject(err);//异步操作处理失败时,rejected
    }).then((res) => {
        console.log(res);//成功时执行
    }).catch((err) => {
        console.log(err);//失败时执行
    })

  resolve作用是,将Promise对象的状态从从 pending 变为 resolved,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

  reject作用是,将Promise对象的状态从 pending 变为 rejected,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise的状态

  promise共有三种状态:pending[待定]初始状态、fulfilled[实现]成功、rejected[被否决]失败。

  promise的状态只有两种改变可能: -从pending变为fulfilled -从pending变为rejected 当任何一种状态改变发生后,promise的状态就将保持凝固不再改变。

Promise的方法与执行

  promise在被创建完成后就会立即执行内部的代码,不论是否被调用。

  通过.then()方法可以在promise变为fulfilled状态后执行某些成功操作。

  通过.catch()方法可以在promise变为rejected状态或promise执行异步代码出错时进行错误或异常处理

    new Promise((resolve, reject) => {
        console.log(new Date() + "pending")
        setTimeout(() => {
            resolve("fulfilled");
            reject("rejected");
        }, 1000);
    }).then((res) => {
        console.log("success");
        console.log(new Date() + res);
    }).catch((err) => {
        console.log("error");
        console.log(new Date() + err);
    });
    console.log(new Date() + "end")

promise执行.jpg

通过如上代码及执行结果很容易即可看出promise创建后就立即执行内部代码,且状态只会有一次改变等特性。

async、await

  async、awiat本身是对promise的进一步优化,只不过在写代码时,Promise的API用的较少,很接近于同步代码的写法。

async

  通过给一个方法添加async关键字,即可标记此方法为异步方法,代表此方法内部可能存在异步调用,也代表此方法内部可以使用await关键字。

  async所修饰的方法返回类型为promise,根据promise的特性可以知道,当没有await关键字的情况下调用async方法,方法会立即并返回一个promise函数,不会中断代码的执行。这和普通函数有本质上的不同,也是使用过程中必须注意的一点。

  async方法内部返回data即return data,就相当于Promise.resolve(data);

  async方法内部无返回,就相当于Promise.resolve(undefined);

await

  await关键字只能在async方法内部使用,且后面必须跟Promise函数。await本意是async wait,等待的是Promise对象变为fulfilled状态及resolve(data)的消息,当await取得data信息后才会顺序执行后面的代码。需要注意的是,await关键字只关心异步过程成功的消息resolve(data),拿到相应的数据data,至于失败消息reject(error),不关心不处理。我们可以通过以下几种方法来处理错误:

(1)让await后面的Promise对象自己catch;

(2)也可以让外面的async函数返回的Promise对象统一catch;

(3)像同步代码一样,放在一个try...catch结构中;

代码验证

    async function asyncFunction1() {
        console.log(new Date() + 'asyncFunction1');
        return await new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('asyncFunction1');
                console.log(new Date() + 'asyncFunction1 resolved');
            }, 1000);
        });
    }

    async function asyncFunction2() {
        console.log(new Date() + 'asyncFunction2');
        return await new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('asyncFunction2');
                console.log(new Date() + 'asyncFunction2 resolved');
            }, 2000);
        });
    }

    async function test() {
        console.log(new Date() + 'test start');
        let a= asyncFunction1();
        console.log(new Date() + 'asyncFunction1 called');
        console.log(new Date() + a);
        let b = await asyncFunction2();
        console.log(new Date() + 'asyncFunction2 called');
        console.log(new Date() + b);
    }

    test();

image.png

Observable

  Observable(可观察对象)是RXJS基于观察者模式(发布订阅模式:Publish/Subscribe)以函数式编程思维来实现的。其可以用来处理异步事件,在Angular中,所有的HTTP请求返回的都是Observable。 在创建Observable对象时,需要传入一个函数(observer)作为构造函数的参数,此函数也被称为订阅者函数,是生产者向消费者推送消息的媒介。

        new Observable(observer => {
            observer.next();
            observer.error();
        });

Observable在消费者订阅(subscribe)以前,订阅者函数(observer)不会执行。消费者可以在任意时刻停止订阅。

        new Observable(observer => {
            observer.next();
            observer.error();
            observer.unsubscribe();
        }).subscribe().unsubscribe();

Observable的状态可以改变多次,即在通过complete()标记Observable已完成之前,我们可以通过订阅者函数(observer)多次传递消息。 -当消费者在通过subscribe订阅Observable时,需要指定next、error消息的对应处理函数。

        new Observable().subscribe((next) => {
            console.log(next);
        }, error => {
            console.log(error);
        });

observer(观察者)

  observer是一个对象,其中包含next、error、complete三个属性,这三个属性均是函数。

  • next:当一切正常的情况执行
  • error:当出现错误时执行
  • complete:传输完成的情况下执行

代码验证

  自RXJS6以后,Observable.create方法已经过时,我们需要通过new Observable()方法来创建Observable。

import {Observable} from 'rxjs';
function test() {
    console.log(new Date() + '开始创建Observable');
    const a = new Observable(observer => {
        try {
            console.log(new Date() + '开始执行Observable代码');
            observer.next('第一次返回成功信息');
            console.log(new Date() + '第一次返回成功信息');
            setTimeout(() => {
                observer.next('第二次返回成功信息');
                console.log(new Date() + '第二次返回成功信息');
            }, 1000);
            throw Error('错误');
        } catch (e) {
            observer.error('第一次返回错误信息');
            console.log(new Date() + '第一次返回错误信息');
            setTimeout(() => {
                observer.error('第二次返回错误信息');
                console.log(new Date() + '第二次返回错误信息');
            }, 3000);
        }
    });
    console.log(new Date() + 'Observable创建完成');
    a.subscribe((next) => {
        console.log(new Date() + '接收到成功信息:' + next);
    }, error => {
        console.log(new Date() + '接收到错误信息:' + error);
    });
}
test();

image.png