promise、async函数、异步打印的面试题理解

有基础的直接看题,不要浪费时间看我从各处复制来的概念

记一个分析的题目

    console.log(1)

    async function fun1() {
        console.log(2)
        let a =await fun2()
        setTimeout(function(){
            console.log(a,3)
        })
        await fun3();
        console.log(4)
    }
    fun1()
    async function fun2() {
        console.log(5)
        return 0
    }
    async function fun3() {
        console.log(6)
    }
    let p = new Promise(function (resolve, reject) {
        console.log(7)
        resolve(8)
    })
    p.then((data) => {
        console.log(data)
    })

    console.log(9)
  1. 首先执行第一个console,打印1
  2. 遇到fun1(),执行fun1,此时打印2,遇到await,需要执行await后跟随的方法fun2,打印5,其后代码加入微任务队列
  3. 继续向下执行遇到实例化promise,实例化内部的function是同步执行的,所以打印7,遇到then方法,将其加入微任务队列
  4. 打印9
  5. 回到任务队列中,第一个加入微任务队列的是第二步中的fun2执行后,此时遇到setTimeout将其加入宏任务队列,遇到await立即执行fun3,打印6,其后代码加入微任务队列
  6. 下一个微任务是在第三步中加入的then方法,打印resolve传递的值,打印8
  7. 回头看微任务和宏任务当前这次任务队列中的微任务和宏任务执行完毕,下一次首先找微任务,执行setTimeout之后的,await fun3之后的打印4
  8. 打印微任务完成只剩最后一个setTimeout任务打印0 3

一、js单线程

js的运行是单线程的,除了使用web worker以外,js缺乏多线程处理的能力,面对并发时js有自己的独门秘籍:EventLoop

二、事件循环

事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。
截取MDN的一段说明: 原文地址
每个代理都是由事件循环驱动的,事件循环负责收集用事件(包括用户事件以及其他非用户事件等)、对任务进行排队以便在合适的时候执行回调。然后它执行所有处于等待中的 JavaScript 任务(宏任务),然后是微任务,然后在开始下一次循环之前执行一些必要的渲染和绘制操作。

网页或者 app 的代码和浏览器本身的用户界面程序运行在相同的 线程中, 共享相同的 事件循环。 该线程就是 主线程,它除了运行网页本身的代码之外,还负责收集和派发用户和其它事件,以及渲染和绘制网页内容等。
用图片来展示
图片来自MDN
js代码执行的过程是从上而下的

console.log(0)
function func1(num){
	console.log(num)
}
console.log(2)
func1(1)
console.log(3)
// 0
// 2
// 1
// 3

js会将需要执行的方法放入一个队列中,运行时先处理最先进入队列的消息,被处理的消息就会被移除这个队列。当函数处理完成就会调用下一个进入队列的消息(如果还有的话)。

事件循环的一些特点

优点:在事件循环中只有当前消息完整的执行后,才会执行下一个消息,这为js的函数提供了一个优秀的能力,例如只有当前函数处理完成数据的处理才会去运行其他的代码,其他函数才能修改操作的数据。
缺点:因为对执行顺序的严格要求也带来了一些问题,比如当一个函数处理时间较长的时候,其他的程序将等待完成以后才可以执行,这时候web就无法响应dom与用户的交互,列如滚动等操作。这时候就需要编写的程序对这些情况做对应的处理。

三、同步与异步

在事件循环的过程中,最重要的就是认识同步与异步,对于js来说,很多的js都是同步运行的,就像上面运行的例子一样,js从上到下逐步打印,这就是一个同步执行的过程。
js也提供了异步编程的能力,异步指两个或两个以上的对象或者事件不同时存在或发生(或多个相关事物的发生无需等待其前一事物的完成),异步基本是用在两个环境中,网络与通信软件设计
在js中由于单线程的限制,异步就显的更加重要,对于传统意义上的js单线程一旦被阻塞就无法继续执行,即使 加入了worker线程,但是也有很多局限性,比如当前的结果依赖于前置的几个计算结果,无法操作dom。例如一个耗时的Ajax请求,当采用异步请求时就避免了整个页面等待的尴尬,将异步任务加入独立的队列中等待处理防止阻塞的发生。

异步的简介

如果程序就这样一步步的执行下去,条例清晰,但是当我们遇到阻塞时,比如一个耗时的Ajax请求,或者读写文件之类的操作,由于js是单线程的,其他后续的操作也都将被阻塞,我们就需要异步的进行一些操作

var response = fetch('myImage.png');
var blob = response.blob();
// display your image blob in the UI somehow

对于上面的代码来说,fetch提供了异步读取网络文件的能力,相当于将fetch行为加入了异步队列中,只能等待同步的代码执行完成之后才会开始清理异步队列的任务。这就是一个异步编程,但是这是有问题的,因为fetch行为是处于异步队列的,后续的代码不能确定什么时候获取到response,此时引入callback(回调函数),或者promise

异步callbacks

异步callbacks其实就是函数,只不过作为参数传递给那些异步执行的其他函数,当异步执行的函数完结时就调用回调函数

const btn = document.querySelector('button');
btn.addEventListener('click', () => {
  alert('You clicked me!');

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});

对于上面的调用,第二个参数就是一个回调函数,在btn被触发click之后执行,这种方式也可以自己注册一个事件监听,回调会有很多妙用

Promises

promise用来表示一个异步操作的最终完成(或者失败)以及结果值

let myFirstPromise = new Promise(function(resolve, reject){
    //当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
    //在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
    setTimeout(function(){
        resolve("成功!"); //代码正常执行!
    }, 250);
});

myFirstPromise.then(function(successMessage){
    //successMessage的值是上面调用resolve(...)方法传入的值.
    //successMessage参数不一定非要是字符串类型,这里只是举个例子
    console.log("Yay! " + successMessage);
});

在异步任务中调用resolve/reject将异步任务的结果和状态传递出来,在then方法中也是使用回调函数的形式来处理传递出来的值。

async,await

async函数是使用async关键字声明的函数。
形如:

    async function fun1() {
        let a = await fun2()
        console.log(a) // 1
    }


    fun1()

    async function fun2() {
        return 1
    }
    console.log(fun2()) // Promise {<fulfilled>: 1}

async函数返回的是一个Promise对象的实例,在内部可以通过await表达式暂停整个async函数的执行进程

四、宏任务与微任务

宏任务包含setTimeout,setInterval,I/O,postMessage等,微任务包含:Promise.then,,Object.observe,MutationObserver,process.nextTick(Node.js 环境)等,await执行后跟随的代码也是一个微任务
对于js来说同处于同一个任务队列中,优先执行微任务

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霜叶w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值