关于setTimeout和Promise执行顺序问题

先看一段代码

console.log('打印'+1);
setTimeout(function(){
    console.log('打印'+2);
})
new Promise(function(resolve,reject){
        console.log('打印'+3);
      }).then(
  console.log('打印'+4));;
//注意此处和下面的区别
  //  resolve();
  // }).then(function () {
  //     console.log(4);
  // }
 // );

console.log(
'打印'+10); new Promise(function(resolve,reject){ setTimeout(function () { console.log('打印'+5); }); }).then( console.log('打印'+6)); setTimeout(function(){ new Promise(function(resolve,reject){ console.log('打印'+7); }); })

执行结果:

console.log('打印'+1);
setTimeout(function(){
    console.log('打印'+2);
})
new Promise(function(resolve){
        console.log('打印'+3);
        resolve();
      }).then(function(){
        console.log(4);
      }
  );
console.log('打印'+10);
new Promise(function(resolve){
      setTimeout(function () {
        console.log('打印'+5);
      });
      resolve();
  }).then(function(){

  console.log('打印'+6)});
setTimeout(function(){
    new Promise(function(resolve){
        console.log('打印'+7);
      });
})
//执行结果:
//1;3;10;4;6;2;5;7

可以看出Promise比setTimeout()先执行。

因为Promise定义之后便会立即执行,其后的.then()是异步里面的微任务。

而setTimeout()是异步的宏任务。

 

引自https://www.cnblogs.com/woodyblog/p/6061671.html 

js是单线程语言,但js的宿主环境(比如浏览器,Node)是多线程的,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性。

浏览器

js是单线程语言,浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。

任务队列

刚才说到浏览器为网络请求这样的异步任务单独开了一个线程,那么问题来了,这些异步任务完成后,主线程怎么知道呢?答案就是回调函数,整个程序是事件驱动的,每个事件都会绑定相应的回调函数,举个栗子,有段代码设置了一个定时器

setTimeout(function(){
    console.log(time is out);
},1000);

执行这段代码的时候,浏览器异步执行计时操作,当1000ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。整个程序就是通过这样的一个个事件驱动起来的。
所以说,js是一直是单线程的,浏览器才是实现异步的那个家伙。

导图要表达的内容用文字来表述的话:

  • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
  • 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
  • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

 

主线程

js一直在做一个工作,就是从任务队列里提取任务,放到主线程里执行。下面我们来进行更深一步的理解。

 

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的第三步。

只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。

Event Loop

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

为了更好地理解Event Loop,请看下图(转引自Philip Roberts的演讲《Help, I'm stuck in an event-loop》)。

上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

异步任务有宏任务和微任务。

2.宏任务macrotask:

(事件队列中的每一个事件都是一个macrotask)

优先级:主代码块 > setImmediate > MessageChannel > setTimeout / setInterval

比如:setImmediate指定的回调函数,总是排在setTimeout前面(!!!!https://segmentfault.com/a/1190000013102056?utm_source=tag-newest)

setImmediate()具有最高优先级,只要poll队列为空,代码被setImmediate(),无论是否有timers达到下限时间,setImmediate()的代码都先执行。

但是

setTimeout(() => {
    console.log('setTimeout');
}, 0);
setImmediate(() => {
    console.log('setImmediate');
})
//setTimeout
//setImmediate



setImmediate(() => {
    console.log('setImmediate');
})
setTimeout(() => {
    console.log('setTimeout');
}, 0);
//setImmediate
//setTimeout
setImmediate只有ie10以上的IE支持

3.微任务包括:

优先级:process.nextTick > Promise > MutationObserver

下面这个代码输出结果是什么?

主程序和和settimeout都是宏任务,两个promise是微任务

第一个宏任务(主程序)执行完,执行全部的微任务(两个promise),再执行下一个宏任务(settimeout),所以结果为:

执行结果:

 

        function china() {
            var p = new Promise(
                function (resolve, reject) {                   
                    setTimeout(function () {
                        console.log('中国  国家')                       
                    }, 1000)
                    resolve('教育大省份');
                    console.log(1);
                }
            )
            console.log('china中国');
            return p;
        }

        function jiangshu(data) {       
            var p = new Promise(function (resolve, reject) {
                setTimeout(function () {
                    console.log('陕西 省份')               
                }, 2000)
                resolve('地级市');
                console.log(2);
            })
            console.log('渭南' + data);
            return p;
        }

        function xian(data) {
            console.log('白水县县' + data)
            var p = new Promise(function (resolve, reject) {
                setTimeout(function () {
                    console.log('林皋镇');
                }, 2000)
                resolve('尧禾镇')
                console.log(3);
            })
            return p;
        }

        china().then(jiangshu).then(xian).then(function (data) {
            console.log(data)
        })

 

posted @ 2018-08-31 11:21  sunmarvell  阅读(21346)  评论(2编辑  收藏  举报