js中的异步操作

301 阅读3分钟

Js中的异步操作:

众所周知,javascript是一种单线程语言,实现起来比较简单,但是只要有一个任务正在执行,那其他任务只能等待这个任务执行完成才能继续,严重影响执行效率,为了解决这种问题,js创建了异步执行模式。

回调函数:

回调函数是实现异步模式的最基础的实现方法,以setTimeout为例,将f2函数作为参数传入f1函数中(传入的是函数名f2,而不是f2()),推迟f2的执行,就不会堵塞后续任务的执行。

function f1(callback) {setTimeout(function(){callback()},10000)};
f1(f2);

Promise对象

Promise 是异步编程的一种解决方案,相对于回调函数而言,能够监测异步函数的执行状态,更合理且更强大。Promise主要有三种状态:pending,fulfilled,rejected,分别对应进行中,成功和失败。

Promise创建:

Promise构造函数中主要有两个参数:resolve和reject,分别是异步操作成功和异步操作失败后调用的函数,这两个函数都会将结果传递出去,在对象创建完成之后,就可以通过.then和.catch分别传入对应的处理函数。

var promise = new Promise(function(resolve, reject){
    if (/* 异步操作成功 */) {resolve(value);}
    else { reject(error);}
})
promise.then((value)=>{console.log(value)})//作为resolve回调函数传入
.catch((error)=>{console.log(error)})//作为reject回调函数传入

执行顺序:

let promise = new Promise(function(resolve, reject){console.log("AAA");resolve()});
promise.then(() => console.log("BBB"));
console.log("CCC")

执行输出为AAA=>CCC=>BBB,表明Promise在新建后会立即执行,所以首先输出 AAA。然后,then方法指定的回调函数将在当前脚本所有同步任务执行完后才会执行,所以BBB 最后输出。

一般axios的请求都会以promise的形式传递给前端参数,然后使用.then()进行相应的处理,而我们在.then之外是得不到结果的,因为异步执行会将当前的同步任务执行完成后才会执行。页面上可以显示是因为渲染watcher的作用,包括watch和computed也是这个道理。

链式结构:

then()方法会返回一个新的Promise实例,所以then()方法后面可以继续跟另一个then()方法进行链式调用。前一个then如果有返回值,该返回值将继续作为参数传入到下一个then

promise.then((res) => {console.log(res);return res;})
.then((val)=>{console.log(val)})

async/await:

async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成,并且await 只能出现在 async 函数中(后面会解释)。

async:

async 函数会返回一个 Promise 对象。如果返回值不是一个promise对象,async也会强制封装成 Promise 对象,然后再返回。由于返回的是promise对象,那后面就可以通过then方法来处理。

async function test() {
    return "123";
}
console.log(test()) //输出 Promise {<fulfilled>: '123'}
test().then(v => {console.log(v); // 输出 123});

await:

正常情况下,await命令后面是一个Promise对象,会等待返回该对象的结果。如果后面跟着的不是promise对象,await也会进行等待,等待的后果就是会阻塞后面任务的执行,所以await就只能用在async函数中,相对于链式处理,await的优势在于更加简洁方便。借边城老师的例子一用:

/**
 * 传入参数 n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n + 200,这个值将用于下一步骤
 */
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}
function step1(n) {
    return takeLongTime(n);
}

function step2(m, n) {
    return takeLongTime(m + n);
}

function step3(k, m, n) {
    return takeLongTime(k + m + n);
}

如果使用链式来调用,中间还会涉及到promise对象嵌套:

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => {
            return step2(time1, time2)
                .then(time3 => [time1, time2, time3]);
        })

        .then(times => {
            const [time1, time2, time3] = times;
            return step3(time1, time2, time3);
        })

        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

如果使用async/await,一下子就简洁好多,舒服~

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time1, time2);
    const result = await step3(time1, time2, time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();