javascript之事件回调

723 阅读2分钟

winter 老师《重学前端》学习笔记

当拿到一段js代码,浏览器或node环境首先要做的就是,传递给js引擎,并且要求它去执行。ES5之后js中引入了promise,这样不需要浏览器的安排,js引擎本身也可以发起任务了。

把宿主发起的任务成为宏观任务,把js引擎发起的任务成为微观任务

宏观和微观任务

在操作系统中,js引擎等待宿主环境分配宏观任务,通常等待的行为都是一个事件循环。

while(true){
	r = wait();
    executer(r);
}

整个循环要做的事情基本就是反复“等待--执行”。这里每次的执行都是一个宏观任务,宏观任务的队列就相当于事件循环。宏任务中,js的promise还会产生异步代码,js保证这些异步代码在一个宏任务中完成,因此,每个宏观任务中又包含一个微观任务队列

有了宏观任务和微观任务机制,Promise 永远在队列尾部添加微观任务;setTimeout 等宿主 API,则会添加宏观任务。

promise

promise是js语言提供的一种标准化的异步管理方式。总体思想:需要异步操作的函数,不返回真实结果,而返回一个“ 承诺”,等待“承诺”兑现函数就会被调用(promise的then方法的回调)

setTimeout(() => console.log("d"), 0);  
var r = new Promise(function (resolve, reject) { 
    resolve() 
}); 

r.then(() => { 
    var begin = Date.now(); 
    while (Date.now() - begin < 1000); 
    console.log("c1");       
    new Promise(function (resolve, reject) { 
        resolve() 
    }).then(() => console.log("c2")) 
}).then(()=> {
    console.log("c3")
}).then(()=> {
    console.log("c4")
})

var f = new Promise(function (resolve, reject) {
    resolve()
});

f.then(() => {
    var begin = Date.now();
    while (Date.now() - begin < 1000);
    console.log("f1");
    new Promise(function (resolve, reject) {
        resolve()
    }).then(() => console.log("f2"))
}).then(() => {
    console.log("f3")
}).then(()=> {
    console.log("f3")
})

输出结果:c1 f1 c2 c3 f2 f3 c4 f4 d

即使强制1s的执行耗时,c仍然优先于d执行。会把本次宏任务的所有微任务执行完,再执行下一个宏任务。

function sleep(time) {
    return new Promise((resolve, reject)=> {
        console.log('b');
        setTimeout(resolve, time);
    })
}

console.log('a')
sleep(5000).then(()=> console.log('c'))

输出结果: a b c

总结异步任务执行的顺序

  • 首先分析有多少个宏任务
  • 在每个宏任务中,分析有多少个微任务
  • 根据调用次序,确定宏任务中的微任务执行次序
  • 根据宏任务的触发规则和调用次序,确定宏任务的执行次序
  • 确定整个顺序

新特性:async/await

async函数必定返回promise,把所有返回promise的函数都可以认为是异步函数

function sleep(time){
    return new Promise((resolve, reject)=> {
        setTimeout(resolve, time);
    })
}

async function foo(name){
    await sleep(2000);
    console.log(name);
}

async function foo2(){
    await foo('a');
    console.log('c');
    await foo('b');
}

foo2();

输出结果: a c b

此外,generator/iterator 也常常被跟异步一起来讲,但是 generator 并非被设计成实现异步,所以有了 async/await 之后,generator/iterator 来模拟异步的方法应该被废弃。

制作红绿灯

现在要实现一个红绿灯,把一个圆形 div 按照绿色 3 秒,黄色 1 秒,红色 2 秒循环改变背景色

<div id="circle" style="width: 100px;height: 100px;border-radius: 100px;background-color: green;"></div>
<script>
    document.addEventListener('DOMContentLoaded', function(){
        function sleep(time){
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve();
                }, time);
            })
        }
        async function changeColor(color, time){
            document.getElementById('circle').style.backgroundColor = color;
            await sleep(time);
        }
        async function main(){
            while (true) {
                await changeColor('green', 3000)
                await changeColor('yellow', 1000)
                await changeColor('red', 2000)           
            }
        }
        main()
    })
</script>