Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript执行:Promise 里的代码为什么比 setTimeout 先执行? #8

Open
K-Kevin opened this issue Oct 31, 2019 · 0 comments

Comments

@K-Kevin
Copy link
Owner

K-Kevin commented Oct 31, 2019

ref:《重学前端》

一个 JavaScript 引擎会常驻于内存中,它等待着我们(宿主)把 JavaScript 代码或者函数传递给它执行。

在ES3和更早的版本中,JavaScript本身还没有异步执行代码的能力,这也就意味着,宿主环境传递给JavaScript引擎一段代码,引擎就把代码直接顺次执行了,这个任务也就是宿主发起的任务。

但是,在ES5之后,JavaScript引入了Promise,这样,不需要浏览器的安排,JavaScript引擎本身也可以发起任务了。

Promise

基本用法

    function sleep(duration) {
        return new Promise(function(resolve, reject) {
            setTimeout(resolve,duration);
        })
    }
    sleep(1000).then( ()=> console.log("finished"));

再看下执行顺序

    var r = new Promise(function(resolve, reject){
        console.log("a");
        resolve()
    });
    r.then(() => console.log("c"));
    console.log("b")

上面输出是 a b c,在进入 console.log("b") 之前, r 已经得到了resolve,但是Promise的resolve始终是异步操作,所以c无法出现在b之前。

下面是 setTimeout 混合 Promise。

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

输出是 a b c d,不论代码顺序如何,d必定发生在c之后,因为Promise产生的是JavaScript引擎内部的微任务,而setTimeout是浏览器API,它产生宏任务。

为了理解微任务始终先于宏任务,我们设计一个实验:执行一个耗时1秒的Promise。

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

这里我们强制了1秒的执行耗时,这样,我们可以确保任务c2是在d之后被添加到任务队列。

我们可以看到,即使耗时一秒的c1执行完毕,再enque的c2,仍然先于d执行了,这很好地解释了微任务优先的原理。

新特性:async/await

async/await是ES2016新加入的特性,它提供了用for、if等代码结构来编写异步的方式。它的运行时基础是Promise,先来看一下基本用法。

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

async函数是一种特殊语法,特征是在function关键字之前加上async关键字,这样,就定义了一个async函数,我们可以在其中使用await来等待一个Promise。

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(){
    console.log("a")
    await sleep(2000)
    console.log("b")
}

async函数强大之处在于,它是可以嵌套的。我们在定义了一批原子操作的情况下,可以利用async函数组合出新的async函数。

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(name){
    await sleep(2000)
    console.log(name)
}
async function foo2(){
    await foo("a");
    await foo("b");
}
@K-Kevin K-Kevin changed the title Promise 里的代码为什么比 setTimeout 先执行? JavaScript执行:Promise 里的代码为什么比 setTimeout 先执行? Oct 31, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant