异步&事件循环输出题-易错知识点

1

const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);
// 1、2、4

Promise 的执行器函数是同步执行的,1、2会在4前面;没有resolve、 reject,Promise 状态一直是pending,.then不执行。

2

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);
// 结果
promise1
1 Promise{<resolved>: resolve1}
2 Promise{<pending>}
resolve1

需要注意的是,直接打印promise1,会打印出它的状态值和参数。
1、script是一个宏任务,按照顺序执行这些代码;
2、首先进入Promise,执行该构造函数中的代码,打印promise1;
3、碰到resolve函数, 将promise1的状态改变为resolved, 并将结果保存下来;
4、碰到promise1.then这个微任务,将它放入微任务队列;
5、promise2是一个新的状态为pending的Promise;
6、执行同步代码1, 同时打印出promise1的状态是resolved;
7、执行同步代码2,同时打印出promise2的状态是pending;
8、宏任务执行完毕,查找微任务队列,发现promise1.then这个微任务且状态为resolved,执行它

console.log(‘1’, promise1)这里,promise1不一定输出Promise{<resolved>: resolve1},因为在某些旧版工具或特定环境下不会立即计算 Promise 的最终状态。

3

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)
// 结果
1
Promise {<fulfilled>: undefined}

Promise.resolve()的第一个参数。使用这个静态方法,实际上可以把任何值都转换为一个期约:

console.log(Promise.resolve(3))
//
Promise { 3 }

Promise.prototype.then()是为期约实例添加处理程序的主要方法。这个 then()方法接收最多两个参数:onResolved 处理程序和 onRejected 处理程序。传给 then()的任何非函数类型的参数都会被静默忽略。如果想只提供 onRejected 参数,那就要在 onResolved 参数的位置上传入 undefined。这样有助于避免在内存中创建多余的对象,对期待函数参数的类型系统也是一个交代。

// 不传 onResolved 处理程序的规范写法
p2.then(null, () => onRejected('p2'));

若调用 then()时不传处理程序,则原样向后传:

let p1 = Promise.resolve('foo'); 
// 若调用 then()时不传处理程序,则原样向后传
let p2 = p1.then();
setTimeout(console.log, 0, p2); // Promise <resolved>: foo

4

let p1 = Promise.resolve('foo');
let p10 = p1.then(() => { throw 'baz'; }); 
// Uncaught (in promise) baz 
setTimeout(console.log, 0, p10); // Promise <rejected> baz

抛出异常会返回拒绝的期约throw 'baz';

5

const promise = Promise.resolve().then(() => {
  return promise;
})
promise.catch(console.err)

// 
Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。

6

Promise.resolve('1')
  .then(res => {
    console.log(res)
  })
  .finally(() => {
    console.log('finally')
  })
Promise.resolve('2')
  .finally(() => {
    console.log('finally2')
    return '我是finally2返回的值'
  })
  .then(res => {
    console.log('finally2后面的then函数', res)
  })
//
1
finally2
finally
finally2后面的then函数 2

7

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
function runReject (x) {
  const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
  return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
       .then(res => console.log(res))
       .catch(err => console.log(err))

//结果
// 1s后输出
1
3
// 2s后输出
2
Error: 2
// 4s后输出
4

Promise.all如果所有期约都成功解决,则合成期约的解决值就是所有包含期约解决值的数组,按照迭代器顺序;
如果有期约拒绝,则第一个拒绝的期约会将自己的理由作为合成期约的拒绝理由。之后再拒绝的期约不会影响最终期约的拒绝理由。不过,这并不影响所有包含期约正常的拒绝操作。合成的期约会静默处理所有包含期约的拒绝操作

8

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log('result: ', res))
  .catch(err => console.log(err))

//
1
'result: ' 1
2
3

Promise.race无论是解决还是拒绝,只要是第一个落定的期约,Promise.race()就会包装其解决值或拒绝理由并返回新期约。其他的期约正常执行。

9

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

//
script start
async1 start
promise1
script end

这里需要注意的是在async1中await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,所以在await之后的内容是不会执行的,包括async1后面的 .then。

10

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");
}, 0);

async1();

new Promise(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

事件循环的工作方式大致如下:
1、执行栈(Stack)是空的,浏览器从宏任务队列(Macrotask Queue)中取出一个宏任务执行。
2、执行宏任务的过程中,如果遇到微任务(例如通过Promise.then、process.nextTick(Node.js)、MutationObserver(浏览器)等产生的),则将这些微任务添加到微任务队列(Microtask Queue)中。
3、当前宏任务执行完毕后,会查看微任务队列是否有微任务。如果有,则立即执行所有的微任务,直到微任务队列清空
4、微任务队列清空后,事件循环会再次从宏任务队列中取出一个宏任务执行,重复上述步骤。

11

async function async1 () {
  await async2();
  console.log('async1');
  return 'async1 success'
}
async function async2 () {
  return new Promise((resolve, reject) => {
    console.log('async2')
    reject('error')
  })
}
async1().then(res => console.log(res))

//
async2
Uncaught (in promise) error

如果async函数中抛出了错误,就会终止错误结果,不会继续向下执行。

12

期约已经变为resolved状态,所以后面的resolve()不会再执行

13

const p1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('resolve3');
    console.log('timer1')
  }, 0)
  resolve('resovle1');
  resolve('resolve2');
}).then(res => {
  console.log(res)  // resolve1
  setTimeout(() => {
    console.log(p1)
  }, 1000)
}).finally(res => {
  console.log('finally', res)
})

//
resolve1
finally  undefined
timer1
Promise{<resolved>: undefined}

Promise.prototype.then()是为期约实例添加处理程序的主要方法。这个 then()方法接收最多两个参数:onResolved 处理程序和 onRejected 处理程序。传给 then()的任何非函数类型的参数都会被静默忽略。

处理程序的返回值会通过Promise.resolve()包装来生成新期约。如果没有提供这个处理程序,则 Promise.resolve()就会包装上一个期约解决之后的值。如果没有显式的返回语句,则 Promise.resolve()会包装默认的返回值 undefined。如下:

let p1 = Promise.resolve('foo'); 
// 若调用 then()时不传处理程序,则原样向后传
let p2 = p1.then();
setTimeout(console.log, 0, p2); // Promise <resolved>: foo

// 没有显示的返回值,隐式地返回 undefined
let p3 = p1.then(() => undefined); 
let p4 = p1.then(() => {}); 
let p5 = p1.then(() => Promise.resolve());
setTimeout(console.log, 0, p3); // Promise <resolved>: undefined 
setTimeout(console.log, 0, p4); // Promise <resolved>: undefined 
setTimeout(console.log, 0, p5); // Promise <resolved>: undefined

// 有显示返回值
let p6 = p1.then(() => 'bar'); 
let p7 = p1.then(() => Promise.resolve('bar')); 
setTimeout(console.log, 0, p6); // Promise <resolved>: bar 
setTimeout(console.log, 0, p7); // Promise <resolved>: bar

14

Promise.resolve().then(() => {
    console.log('1');
    throw 'Error';
}).then(() => {
    console.log('2');
}).catch(() => {
    console.log('3');
    throw 'Error';
}).then(() => {
    console.log('4');
}).catch(() => {
    console.log('5');
}).then(() => {
    console.log('6');
});
//
1 
3 
5 
6

抛出异常会返回拒绝的期约,返回错误值不会触发拒绝行为。.catch()方法用于给期约添加拒绝处理程序。这个方法只接收一个参数:
onRejected 处理程序。事实上,这个方法就是一个语法糖,调用它就相当于调用 Promise.prototype. then(null, onRejected)。

  • 25
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值