异步

255 阅读5分钟
异步,上下文执行栈,Event Loop,任务队列(task queue),再到Microtask(微任务)、Macrotask/Task(宏任务)

js 引擎(如:V8)的运行原理

www.bilibili.com/video/av377…

jakearchibald.com/2015/tasks-…

浏览器执行如下:

  1. 扫描文件,在执行上下文栈的同步任务,遇到异步任务会加入任务队列中,执行完同步任务后,就会去任务队列中取出需要执行的代码放入执行栈中去执行;
  2. 首先执行Microtask队列,按照队列先进先出的原则,一次执行完所有Microtask队列任务;
  3. 然后执行Macrotask/Task队列,一次执行一个,一个执行完后,检测 Microtask是否为空;
  4. 为空则执行下一个Macrotask/Task;
  5. 不为空则执行Microtask
Microtask (微任务)
- Promise.then
- MutaionObserver
- process.nextTick(Node.js 环境)

Macrotask (宏任务)
- script(整体代码)
- setTimeout
- setInterval
- I/O
- UI交互事件
- postMessage
- MessageChannel
- setImmediate(Node.js 环境)

async

async函数返回一个 Promise 对象, 一旦遇到await就会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。等到异步操作完成,再接着执行函数体内后面的语句

await命令就是内部then命令的语法糖

注意点

第一点,前面已经说过,await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。

// 即使前一个异步操作失败,也不要中断后面的异步操作。
async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

第二点,多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
promise

juejin.cn/post/684490…

segmentfault.com/a/119000002…

es6.ruanyifeng.com/#docs/promi…

  1. 多级then的回调函数是交替执行的
  2. Promise.resolve(...)同步初始化就知道状态,并且第一时间执行里面的语句,异步需要等执行完才知道
console.log(new Promise((resolve, reject)=> {
    setTimeout((a) => {
        console.log(123)
        resolve(a)
    }, 0, 1)
})) // pending
console.log(Promise.resolve('1')) // fulfilled
  1. 初始化promise时,resolve(Promise)时,需要进行拆箱操作microtask 队列执行结束),reject会直接传递给then方法中的rejected回调。
new Promise((resolve, reject) => {
    resolve(Promise.resolve('resolve'))) // 异步拆箱操作
    // reject(Promise.resolve('resolve'))) 直接执行
})
  1. resolve()传入不是函数时会发生穿透
new Promise((resolve, reject) => {
    resolve(Promise.resolve('resolve'))) 
}).then(1).then(res => {
    console.log(res) // 1
})
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log) // 1
  1. then()会根据上一个promise生成一个新promise,生成的状态如下

    1. return 一个同步的值 ,将返回一个resolved状态的Promise对象,即使是return new Error('error!!!')
    2. return promise , 根据这个Promise的状态创建
    3. throw 一个同步异常(return Promise.reject(new Error('error!!!') && throw new Error('error!!!'))返回一个rejected状态的Promise
  2. Promise接收的函数是同步执行的,但是then中的回调函数的执行则是异步的。

  3. .catch.then 第二个参数的简便写法

promise方法

  1. Promise.all
const p = Promise.all([p1, p2, p3]);

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

  1. Promise.race
const p = Promise.race([p1, p2, p3]);

只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

  1. Promise.allSettled()

    方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。

const promises = [
  fetch('/api-1'),
  fetch('/api-2'),
  fetch('/api-3'),
];

await Promise.allSettled(promises);
  1. ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。

  2. 将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。

    1. 参数是一个 Promise 实例

      如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

    2. 参数是一个thenable对象

      Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。

      let thenable = {
        then: function(resolve, reject) {
          resolve(42);
        }
      };
      
    3. 参数不是具有then()方法的对象,或根本就不是对象

      如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved

    4. 不带有任何参数

      Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象

  3. Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

github.com/Advanced-Fr…

settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行; promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;

async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。

题目
const p = Promise.resolve();
(async () => {
    await p;
    console.log('await end');
})();
p.then(() => {
    console.log('then 1');
}).then(() => {
    console.log('then 2');
});

await end
then 1
then 2
await 异步操作完成指promise确定状态
async function async1() {
    await async2();
    console.log('async1 end');
}

async function async2() {
    return 'async2';
    // return Promise.resolve('async2');
}

async1();

new Promise(function(resolve) {
    resolve();
}).then(function() {
    console.log('Promise then');
});

async1 end --> Promise then
return ‘async2’时,执行代码相当于如下:
new Promise(function (resolve) {
    resolve('async2');
}).then(function() {
    console.log('async1 end');
});

Promise then --> async1 end
return Promise.resolve('async2')时,执行代码相当于如下:
new Promise(function (resolve) {
    resolve(Promise.resolve('async2'));
}).then(function() {
    console.log('async1 end');
});

// 今日头条面试题
    async function async1() {
        console.log('async1 start')
        await async2()
        console.log('async1 end')
    }
    async function async2() {
        console.log('async2')
    }
    console.log('script start')
    setTimeout(function () {
        console.log('settimeout')
    })
    async1()
    new Promise(function (resolve) {
        console.log('promise1')
        resolve()
    }).then(function () {
        console.log('promise2')
    })
    console.log('script end')

    script start
    async1 start
    async2
    promise1
    script end
    async1 end
    promise2
    settimeout