async和await的处理机制

102 阅读4分钟

async、await

ES7 新出的异步编程 API。

// 函数修饰符,控制函数返回 promise 实例
//   + 函数内部执行报错,则返回失败的 promise 实例,值是失败的原因
//   + 如果自己返回一个 promise,以自己返回的值为主
//   + 返回一个非 promise 的话,会自动包装成 promise
//   + 如果函数内部做了异常捕获,则还是成功态
async function fn() {
    console.log(a);
    return 10;
}

console.log(fn());

使用 async 的主要目的,是为了在函数内部使用 await

function api(interval) { 
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(interval);
            console.log(interval);
        }, interval)
    })
}

// 这么写的话,我们发现 1s 后输出 1000,再过 2s 后 输出 3000
// 怎么改成串行的呢,await 就可以做这件事
function func() {
    api(1000);
    api(3000);
}

// await 后面跟的是 promise 实例 「如果不是,浏览器会帮我们转成 promise 实例」
// await 返回一个成功态的值
//   + 比如 var data = await 1 => Promise.resolve(1), data 为 1
// await 是异步微任务
//   + 函数体中遇到 await,后面代码该咋地咋地,但是(左边和下面的代码)会当成一个微任务
async function func () {
    await api(1000);
    await api(3000);
}

func();

await 一个失败的 promise

失败态的 promise 会切断 await 后面冻结的代码执行,包括 await 语句左边的 console 或者 赋值

// await 表达式会暂停整个 async 函数 的执行进程并让出其控制权
// await 中断它之后的代码(包括左边的代码)执行
//   + await 后面的 priomise 实例是成功态之后,才会把之前暂停的代码执行
//   + 如果后面的 promise 实例是失败的,则下面的代码就不再执行了
function api(interval) { 
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // console.log(xxx); // 等同于 rejected
            reject(1);
        }, interval)
    })
}

// 这么写的话,我们发现 1s 后输出 1000,再过 2s 后 输出 3000
// 怎么改成串行的呢,await 就可以做这件事
async function func () {
    console.log(await api(1000)); // 不打印 但是内部抛错  Uncaught (in promise) 1
	console.log(2222); //  不执行
}

func(); 

串行的 promise 调用

var resolveAfter2Seconds = function resolveAfter2Seconds() {
    console.log('starting slow promise');
    return new Promise(resolve => {
        setTimeout(function() {
            resolve('slow');
            console.log('slow promise is done');
        }, 2000);
    });
}

var resolveAfter1Seconds = function resolveAfter1Seconds() {
    console.log('starting fast promise');
    return new Promise(resolve => {
        setTimeout(function() {
            resolve('fast');
            console.log('fast promise is done');
        }, 1000);
    });
}

var sequeial = async function sequential() {
    console.log('== SEQUENTIAL START==');
    const slow = await resolveAfter2Seconds();
    console.log(slow);

    const fast = await resolveAfter1Seconds();
    console.log(fast);
}

sequeial();
// == SEQUENTIAL START==
// starting slow promise
// 2s 后 'slow promise is done'
// "slow"
// starting fast promise
// 又 1s 后 fast promise is done
// fast

并行的 promise 调用

var resolveAfter2Seconds = function resolveAfter2Seconds() {
    console.log('starting slow promise');
    return new Promise(resolve => {
        setTimeout(function() {
            resolve('slow');
            console.log('slow promise is done');
        }, 2000);
    });
}

var resolveAfter1Seconds = function resolveAfter1Seconds() {
    console.log('starting fast promise');
    return new Promise(resolve => {
        setTimeout(function() {
            resolve('fast');
            console.log('fast promise is done');
        }, 1000);
    });
}
var concurrent = async function concurrent() {
    console.log('== concurrent START ==');
    const slow = resolveAfter2Seconds();
    const fast = resolveAfter1Seconds();

    console.log(await slow);
    console.log(await fast);
}

concurrent();
// == concurrent START==
// starting slow promise
// starting fast promise
// 1s 后 'fast promise is done'
// 又 1s 后 'slow fast is done'
// slow
// fast
  1. 首先输出 "== concurrent START=="
  2. resolveAfter2Seconds 方法执行,输出 'starting slow promise',然后 slow 延时器作为一个宏任务在 eventTable 注册,并于 2s 后把回调塞进宏任务执行队列。
  3. resolveAfter1Seconds 方法执行,输出 'starting fast promise',然后 fast 延时器作为一个宏任务在 eventTable 注册,并于 1s 后把回调塞进宏任务执行队列。
  4. await slow 执行,需要先计算 slow 结果,再打印,并把之后的代码作为一个微任务在 eventTable 中注册,等 slow 延时器 返回结果(差不多 2s)之后,再将其后代码塞进微任务执行队列(标记为可执行)。
  5. 同步代码执行完毕,微任务队列没有可执行的微任务,检查微任务队列(最快的微任务需要 2s 后可以执行,准确来讲是依赖 slow 延时器返回结果),检查宏任务队列(最快的宏任务需要 1s 后执行),所以先执行 fast 延时器宏任务, 输出 "fast promise is done"
  6. 此时刚过 1s,微任务队列还没有可执行的微任务,检查微任务队列(最快的微任务是依赖 slow 延时器返回结果),检查宏任务队列(最快的宏任务 slow 是需要 1s 后执行),所以 slow 宏任务执行,输出 "slow promise is done"
  7. slow 微任务执行,输出 "slow", 遇到 await fast,创建一个新的 fast 微任务(左边的打印和下面的代码,这里下面没有代码了),放入微任务队列,标记为可执行
  8. slow 微任务执行完毕出列,检查微任务队列,发下还有个 fast 微任务,执行返回 "fast"
  9. fast 微任务执行完毕出列,此时微任务队列清空,检查宏任务队列,发现没有可执行或待执行的宏任务,事件循环结束。

Promise.all 去做并行调用

var resolveAfter2Seconds = function resolveAfter2Seconds() {
    console.log('starting slow promise');
    return new Promise(resolve => {
        setTimeout(function() {
            resolve('slow');
            console.log('slow promise is done');
        }, 2000);
    });
}

var resolveAfter1Seconds = function resolveAfter1Seconds() {
    console.log('starting fast promise');
    return new Promise(resolve => {
        setTimeout(function() {
            resolve('fast');
            console.log('fast promise is done');
        }, 1000);
    });
}

var concurrentPromise = function concurrentPromise() {
    console.log('== CONCURRENT START Promise.all ==');
    return Promise.all([resolveAfter2Seconds(), resolveAfter1Seconds()]).then(
        messages => {
        console.log(messages[0]);
        console.log(messages[1]);
    });
}

concurrentPromise();

// == CONCURRENT START Promise.all ==
// starting slow promise
// starting fast promise
// 1s 后输出 fast promise is done
// 再过 1s 输出 slow promise is done
// slow 
// fast

值得一提的是:

// .then 后的 onfullfilld 和 onRejected 整个是一个异步微任务。
messages => {
    console.log(messages[0]);
    console.log(messages[1]);
}