JS异步编程:几个想不到的异步代码执行结果

1

new Promise(resolve => {
    console.log('promise1');
    resolve();
}).then(() => {
    console.log('then11');
    // return
    new Promise(resolve => {
        console.log('promise2');
        resolve();
    }).then(() => {
        console.log('then21');
    }).then(() => {
        console.log('then22');
    });
}).then(() => {
    console.log('then12');
});
复制代码

结果:
image。png

事件队列执行的基本模型
image。png

一步一步解析:

首先

new Promise(resolve => {
    console.log('promise1');
    resolve();
})
复制代码

里面的执行器函数是同步执行,所以输出 'promise1'

然后执行 resolve() ,同步修改promise的状态和值。然后根据情况是否设置微任务

如果 thenresolve 之前执行,说明 resolve 为异步代码。 then 里面已经存了方法,即先运行 then 存储,后触发 resolve ,那么就设置一个微任务,这个微任务是:执行 .then 里面的方法。

如果 thenresolve 之后执行,如果没有存方法,就像这个例子一样, resolve 是同步的,执行 resolve 的时候还没有执行 thenthen 是后执行的。那么这个微任务就跳过不设置。

然后执行 .then() 方法,

.then(() => {
    console.log('then11');
    // return
    new Promise(resolve => {
        console.log('promise2');
        resolve();
    }).then(() => {
        console.log('then21');
    }).then(() => {
        console.log('then22');
    });
})
复制代码

执行 .then() ,首先看当前实例的状态。

如果是 pendding 状态,那么就把这个方法存起来,什么时候执行 resolve ,再通过 resolve 提供的微任务来执行 then 里面的回调函数。

如果当前实例的状态是成功或者失败(能看出成功还是失败,说明 resolve 已经同步运行了,比如这里的这个例子)。如果确定状态,那就知道到底执行 then 里面的哪个方法。然后将 .then 其中的回调函数设置成一个微任务,等待同步结束后执行

所以这里是把 then 中的回调函数当成看做 微任务1 ,放入微任务队列中,同步结束后执行

image。png

接下来执行第二个 then

.then(() => {
    console.log('then12');
})
复制代码

第二个 then 执行哪个回调函数,取决于上一个 then 的两个回调函数的返回值和是否报错,因为上一个 then() 微任务1还没有执行,同步结束之后才执行,所以这里先把这个方法先存起来,当做一个微任务,我们叫做微任务2。微任务2需要等待微任务1决定状态之后才执行。

image。png

然后同步已经执行完毕,执行完后开始执行微任务1

() => {
    console.log('then11');
    new Promise(resolve => {
        console.log('promise2');
        resolve();
    }).then(() => {
        console.log('then21');
    }).then(() => {
        console.log('then22');
    });
}
复制代码

输出 'then11'

 new Promise(resolve => {
        console.log('promise2');
        resolve();
    })
复制代码

同样的原理,输出 'promise2' ,并且 resolve 修改状态,后面 then 创建微任务3,微任务3等到主线程一空闲就可以执行了,因为已经同步resolve了状态,直接知道了状态,所以主线程一空闲优先立即执行,这时微任务1还没有执行完,没有执行到最下面,所以微任务3要优先与微任务2

//微任务3
() => {
        console.log('then21');
    }
复制代码

接着继续 then

.then(() => {
        console.log('then22');
    })
复制代码

这个then就创建一个微任务4,直到微任务3执行完才可以知道是否执行。

image。png
这时微任务1执行完毕,微任务1执行完,就可以决定最后那个 then 状态,此时要立刻执行微任务2,微任务2的优先级比微任务4要高
image。png

如果任务队列中有个任务,谁先知道可执行(谁先到达)谁就先执行

执行到 console.log('promise2'); resolve(); 这句代码的时候,继续往下执行,要执行两个 .then() ,这时候第一个 then 中的回调函数任务3还不会执行,会放到微任务队列中,什么时候执行完任务23,才会创建微任务43,放到任务队列中,而什么时候任务1执行完了,才会执行已经放到任务队列中的任务2。

如果改成这样:

new Promise(resolve => {
    console.log('promise1');
    resolve();
}).then(() => {
    console.log('then11');
    return new Promise(resolve => {
        console.log('promise2');
        resolve();
    }).then(() => {
        console.log('then21');
    }).then(() => {
        console.log('then22');
    });
}).then(() => {
    console.log('then12');
});
复制代码

image。png

2

setTimeout(() => {
    console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
    console.log(3);
}, 10);
console.log(4);
console.time('AA');
for (let i = 0; i < 90000000; i++) {
    // do soming
}
console.timeEnd('AA'); //=>AA: 70ms 左右
console.log(5);
setTimeout(() => {
    console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
    console.log(8);
}, 15);
console.log(9);
复制代码

image。png

因为执行完同步代码的时候,已经过了70ms,上面两个宏任务时间已经到了,所以执行完同步代码,立刻开始执行可以执行的宏任务的时候,即会把上面两个立刻执行,然后在等8和15毫秒,再执行下面两个新加入的宏任务。

image。png
同步结束后:
image。png

setTimeout(() => {},0)setTimeout(() => {}):定时器设置为0或者不写,也不是立即执行。 浏览器有一个最快处理反应时间(谷歌5-7ms。ie10-17ms),即使写0也要等待5-7ms左右,所以也是异步的

js处理速度很快,相邻两个同步代码的间隔处理仅有0.01ms左右

3

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(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');
复制代码

image。png

执行到 async1() 函数如下

image。png
需要注意的是,执行完 await async2(); 会将后面的代码创建为微任务2,并且已经知道状态了,所以等同步完成后可立即执行

继续运行如下:
image。png
执行then,因为已经知道状态,所以向微任务队列中添加微任务3,同步结束立即执行

4

let body = document.body;
body.addEventListener('click', function () {
    Promise.resolve().then(() => {
        console.log(1);
    });
    console.log(2);
});
body.addEventListener('click', function () {
    Promise.resolve().then(() => {
        console.log(3);
    });
    console.log(4);
});
复制代码

image。png
代码执行,添加两个回调函数为宏任务,然后点击body,2,4为同步任务,1,3为两个宏任务中的微任务。要把一个宏任务完成再进行下一个宏任务,一个宏任务完成之前,要先执行完自己内部的微任务,所以是21先,43后

5

console.log('start');
let intervalId;
Promise.resolve().then(() => {
    console.log('p1');
}).then(() => {
    console.log('p2');
});
setTimeout(() => {
    Promise.resolve().then(() => {
        console.log('p3');
    }).then(() => {
        console.log('p4');
    });
    intervalId = setInterval(() => {
        console.log('interval');
    }, 3000);
    console.log('timeout1');
}, 0);
复制代码

区分宏微任务,同步任务即可

image。png
image。png

6

setTimeout(() => {
    console.log('a');
});
Promise.resolve().then(() => {
    console.log('b');
}).then(() => {
    return Promise.resolve('c').then(data => {
        setTimeout(() => {
            console.log('d')
        });
        console.log('f');
        return data;
    });
}).then(data => {
    console.log(data);
});
复制代码

image。png

b微任务,f同步任务,最后要等return 才能执行最后一个 then ,执行完所有的微任务,再执行两个宏任务。

image。png

7

function func1() {
    console.log('func1 start');
    return new Promise(resolve => {
        resolve('OK');
    });
}
function func2() {
    console.log('func2 start');
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('OK');
        }, 10);
    });
}
console.log(1);
setTimeout(async () => {
    console.log(2);
    await func1();
    console.log(3);
}, 20);
for (let i = 0; i < 90000000; i++) {} //循环大约要进行80MS左右
console.log(4);
func1().then(result => {
    console.log(5);
});
func2().then(result => {
    console.log(6);
});
setTimeout(() => {
    console.log(7);
}, 0);
console.log(8);
复制代码

image。png

image。png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享