异步,上下文执行栈,Event Loop,任务队列(task queue),再到Microtask(微任务)、Macrotask/Task(宏任务)
js 引擎(如:V8)的运行原理
浏览器执行如下:
- 扫描文件,在执行上下文栈的同步任务,遇到异步任务会加入任务队列中,执行完同步任务后,就会去任务队列中取出需要执行的代码放入执行栈中去执行;
- 首先执行Microtask队列,按照队列
先进先出的原则,一次执行完所有Microtask队列任务; - 然后执行Macrotask/Task队列,一次执行一个,一个执行完后,检测 Microtask是否为空;
- 为空则执行下一个Macrotask/Task;
- 不为空则执行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
- 多级then的回调函数是交替执行的
Promise.resolve(...)同步初始化就知道状态,并且第一时间执行里面的语句,异步需要等执行完才知道
console.log(new Promise((resolve, reject)=> {
setTimeout((a) => {
console.log(123)
resolve(a)
}, 0, 1)
})) // pending
console.log(Promise.resolve('1')) // fulfilled
- 初始化promise时,resolve(Promise)时,需要进行拆箱操作( microtask 队列执行结束),reject会直接传递给
then方法中的rejected回调。
new Promise((resolve, reject) => {
resolve(Promise.resolve('resolve'))) // 异步拆箱操作
// reject(Promise.resolve('resolve'))) 直接执行
})
- 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
-
then()会根据上一个promise生成一个新promise,生成的状态如下
return一个同步的值 ,将返回一个resolved状态的Promise对象,即使是return new Error('error!!!')return promise, 根据这个Promise的状态创建throw一个同步异常(return Promise.reject(new Error('error!!!') && throw new Error('error!!!'))返回一个rejected状态的Promise
-
Promise接收的函数是同步执行的,但是then中的回调函数的执行则是异步的。
-
.catch是.then第二个参数的简便写法
promise方法
Promise.all
const p = Promise.all([p1, p2, p3]);
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race
const p = Promise.race([p1, p2, p3]);
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
-
Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是
fulfilled还是rejected,包装实例才会结束。
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises);
-
ES2021 引入了
Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。 -
将现有对象转为 Promise 对象,
Promise.resolve()方法就起到这个作用。-
参数是一个 Promise 实例
如果参数是 Promise 实例,那么
Promise.resolve将不做任何修改、原封不动地返回这个实例。 -
参数是一个
thenable对象Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。let thenable = { then: function(resolve, reject) { resolve(42); } }; -
参数不是具有
then()方法的对象,或根本就不是对象如果参数是一个原始值,或者是一个不具有
then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。 -
不带有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象
-
-
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
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