最近在学习es6的Promise,其中涉及到了Promsie的事件执行机制,因此总结了关于Promise的执行机制,若有错误,欢迎纠错和讨论。

在阮一峰老师的书中《es6 标准入门》对Promise的基础知识做出了详细的介绍,在此就不一一介绍了,直接开始整体,将Promsie中关于事件执行机制的问题与大家分享。

1.Promsie对象的创建以及执行顺序

创建方式:new Promise(function(resolve,reject){ //...}

这种方式在阮老师的书中指出该函数一旦创建,其内部的匿名函数会自动执行,在这里我们可以认为这种方式相当于同步任务会直接执行。以下代码亦可证明。

 new Promise(function(resolve,reject){
console.log('1');
});
console.log('2');

结果:

结论:从结果的顺序来看,的确new Promise会立即执行,执行完后再继续执行后面的同步任务。

2.Promsie对象中的then()方法

Promise中的then()会根据Promsie的状态执行相应的回调函数,方法中有两个参数,分别是回调成功和回调失败时执行的函数。该函数是个异步任务。在此补充下关于JS内部的机制。众所周知,JS是单线程的,也就意味着无法同时做好几件事情,只能一件一件的做。代码执行时分为同步任务和异步任务,同步任务会在当前主线程中完成,执行中将变量和对象放入堆中,方法调用运行压入执行栈,需要变量时从堆中获取。而异步任务又可以分为microtask(微任务)和macrotask(宏任务),可以简单的理解为任务队列,通常将键盘事件和鼠标事件以及setTimeout()等归为宏任务,而我们的Promsie的then()归为微任务,执行中遇到他们会将他们放入相应的任务队列中。

在主线程的任务完成后会立即查看microtask是否有任务需要执行,若有则会执行直到队列为空队列,若本身为空则会查看macrotask任务队列,执行该队列中的第一个命令,当执行完第一条命令后会再次查看microtask是否有代码需要执行,同理,有则执行至空队列否则再次查看macrotask。

注意:这里的过程相当于macrotask任务队列的任务执行一次,则microtask任务队列的任务会全部执行。microtask全部任务执行完才会再去执行macrotask的下一条命令。

 new Promise(function(resolve,reject){
resolve('1');
}).then(function(value){
console.log(value);
}).catch(function(error){
console.log('出错了,',error);
});
console.log('2');

结果:

结论:从上述结果来看,代码会立即执行new Promise中的代码,此时Prosmie状态改为resolved,而后调用then()方法,此时将该函数放入mircotask队列中,向下继续执行同步任务,待同步任务执行完后再回来执行mircotask中的函数。

3.Promsie和setTimeout的执行机制分析

此处直接引入一个例子作为介绍

     console.log('1111111');
setTimeout(() => {
console.log('4----');
new Promise((resolve) => {
console.log('5-----');
resolve();
}).then(() => {
console.log('6----')
})
}) new Promise((resolve) => {
console.log('22222');
resolve();
}).then(() => {
console.log('33333-----')
}) setTimeout(() => {
console.log('77777');
new Promise((resolve) => {
console.log('888');
resolve();
}).then(() => {
console.log('9999999')
})
})

代码分析:

分析之前我们可以先在自己的小本本上写2列事件任务分类,分别是microtask微任务和macrotask宏任务,等下我们在分析代码时候可以模拟引擎的执行顺序将其写入小本子上,便于我们分析。

回归正题,1.首先第一行执行输出打印任务,这个是同步任务没什么说的,直接输出;

2.下一行遇到了setTimeout()事件,先不管内部是什么函数,先将这个函数放入到我们的macrotask任务队列中第一行的位置等待执行;

3.又遇到了new  Promise,之前我们讲到,可以把它看成是同步任务所以直接执行其内部的代码,首先打印输出,之后遇到resovle(),此时Promsie的状态改为resolved,并且值为‘22222’,之后调用then(),该方法是个异步任务,所以同样不管内部代码是什么,将其放入我们的microtask任务队列的第一行中等待执行;

4.之后又遇到了setTimeout(),同样不管其内部代码直接加入macrotask任务中;

此时我们的同步任务执行完毕,我们整理下我们刚刚写下的2个任务队列中都有什么函数,在microtask中,有一个then()方法的函数,在macrotask任务队列中有2个按照执行顺序加入的setTimeout()。

主线程任务执行后会直接去执行microtask的任务,所以下一步开始执行microtask任务队列中的函数,输出打印‘33333-----’,此时microtask任务队列中的任务队列执行完毕并且后面没有其他任务了,此时队列为空,这个时候会去执行macrotask的第一个函数,首先输出打印,遇到Promise对象直接执行,并且状态改为resolved,执行then(),将其函数加入到microtask中,此时该setTImeout执行完毕,这个时候会去再去看microtask是否为空,这个时候发现我们刚刚加入了一个指令,所以直接执行,执行完后microtask又没有任务了,这个时候再去执行macrotask的下一条任务,同样执行完再去执行我们刚刚再次向microtask加入的新任务,至此执行完毕。

大家可以看看是否和大家推到的一样。

4.关于resolve()参数的问题

在这里参数是非promise对象就不探讨了,调用resolve()和reject()就决定了promise对象的状态了。这里讨论下参数是promise对象的问题。首先,给出一段代码。

var p1=Promise.resolve();
var p2=Promise.reject('出错了');
var p3=new Promise(function(resolve,reject){
resolve(p1);
}).then(()=>console.log('p3状态为resolved'),()=>console.log('p3状态为reject'));
var p4=new Promise(function(resolve,reject){
resolve(p2);
}).then(()=>console.log('p3状态为resolved'),()=>console.log('p3状态为reject'));

阮老师在书中指出传入参数为Promsie对象时p1的状态会决定p2的状态。从上面的代码中可以看到,p1和p2直接决定了p3和p4的状态,不会管resolve(),也验证了阮老师的说法。另外,当resolve()改为reject()方法时,我们发现不管参数的状态是什么最后Promise对象的状态都会变为reject。

另外,参数为Promise对象时,引擎会先去获取参数的状态,获得状态在返回决定当前Promise对象的状态,而获取Promsie对象状态的过程我们可以认为是异步,也就是将此过程直接加入到microtask任务队列中。当参数是thenable对象时,会调用其中的then方法,该过程也可以看成是异步的。

    var p1=Promise.resolve();
var p2=new Promise(function(resolve,reject){
resolve(p1);
});
// p2.then(()=>console.log(2));
console.log(p2);
setTimeout(()=>console.log(p2),3000);

 我们发现此时p2的状态并没有直接变为resolved,而是pending,在延迟3s后再判断p2的状态,此时已经变成了resolved,这样证实了我们的想法。参数为thenable对象时也表现出同样的结果。下面代码。

   var p1={
then(resolve,reject){
resolve();
}
}
var p2=new Promise(function(resolve,reject){
resolve(p1);
});
console.log(p2);
setTimeout(()=>console.log(p2),3000);

5.关于resovle(Promise.resolve())的异步执行机制

此处问题也是困扰我好久的一个问题。这里首先需要验证下Promise.resolve()是否是同步的。

    var p1=Promise.resolve();
console.log(p1);
setTimeout(()=>console.log(p1),3000);

从结果来看,p1确实是同步执行的。并且该方法会返回一个新的Promise对象。回到正题,前面讲到resolve()参数是Promsie对象时候去查询参数状态是个异步的,但这个时候我们发现在查询之前参数的状态其实已经确定了,引擎只是去获取状态,而此时我们的resolve(Promise.resolve()),内部并非一个已经确定的Promise对象,而是一个待执行的命令,因此这里会分两步走,第一步,异步执行执行Promise.resolve()命令,此时将新产生的Promsie对象的状态确定下来,name这个时候该函数就变成了前面我们讲到的那个样式,这个时候再执行异步操作获取参数的状态。

下面分析下之前困扰我好久的那个例子:

 new Promise((resolve, reject) => {
console.log("async1 start");
console.log("async2");
resolve(Promise.resolve());
}).then(() => {
console.log("async1 end");
}); new Promise(function(resolve) {
console.log("promise1");
resolve();
}).then(function() {
console.log("promise2");
}).then(function() {
console.log("promise3");
}).then(function() {
console.log("promise4");

除IE外均测试,其结果一致。这里按照我们刚才的思维分析,首先输出async1和async2,此时将Promise.resolve()压入microtask,继续向后执行,遇到新的Promise,输出promise1,改变状态此时将then()的函数压入microtask,这个时候主线程执行完毕,开始执行microtask,Promise.resolve()执行完毕,得到确定状态的新的Promise对象,此时resolve()为获取该Promsie状态将该过程加入到刚才的microtask任务队列中,这个时候任务队列有2个,一个是输出promise2的命令,其后面紧跟获取状态的命令,所以执行输出Promise2的命令,执行完毕后又将下一个then()方法中的Promise3命令添加到获取状态的后面,这个时候再去执行获取状态的命令,得到了状态执行resolve(),触发then(),将输出async end的命令添加到Promise3输出的后面,下一步执行输出Promise3,触发then(),将Promise4输出命令加入到输出async end命令后面,这个时候执行async end输出和Promise4输出,执行完毕。

至此,Promise中的异步机制总结完了,欢迎大家留言指正和批评,和大家一起进步。

es6 Promise 事件机制分析的更多相关文章

  1. java自定义事件机制分析

    import java.util.ArrayList; import java.util.EventListener; import java.util.EventObject; import jav ...

  2. Spring 中的事件机制

    说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作.这些listener是怎 ...

  3. Cocos2d-X3.0 刨根问底(七)----- 事件机制Event源码分析

    这一章,我们来分析Cocos2d-x 事件机制相关的源码, 根据Cocos2d-x的工程目录,我们可以找到所有关于事件的源码都存在放在下图所示的目录中. 从这个event_dispatcher目录中的 ...

  4. Android笔记:触摸事件的分析与总结----TouchEvent处理机制

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://glblong.blog.51cto.com/3058613/1559320   ...

  5. Qt 事件系统浅析 (用 Windows API 描述,分析了QCoreApplication::exec()和QEventLoop::exec的源码)(比起新号槽,事件机制是更高级的抽象,拥有更多特性,比如 accept/ignore,filter,还是实现状态机等高级 API 的基础)

    事件系统在 Qt 中扮演了十分重要的角色,不仅 GUI 的方方面面需要使用到事件系统,Signals/Slots 技术也离不开事件系统(多线程间).我们本文中暂且不描述 GUI 中的一些特殊情况,来说 ...

  6. ApplicationEvent事件机制源码分析

    <spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...

  7. PHP实现事件机制实例分析

    PHP实现事件机制实例分析 内置了事件机制的语言不多,php也没有提供这种功能.事件(Event)说简单了就是一个Observer模式.实现起来非常easy.可是有所不同的是,事件的监听者谁都能够加, ...

  8. React 为什么要把事件挂载到 document 上 & 事件机制源码分析

    前言 我们都知道 React 组件绑定事件的本质是代理到 document 上,然而面试被问到,为什么要这么设计,有什么好处吗? 我知道肯定不会是因为虚拟 DOM 的原因,因为 Vue 的事件就能挂载 ...

  9. ES6 Promise 全面总结

    转载:点击查看原文 ES6 Promise对象 ES6中,新增了Promise对象,它主要用于处理异步回调代码,让代码不至于陷入回调嵌套的死路中. @-v-@ 1. Promise本质 Promise ...

随机推荐

  1. bzoj 4664: Count

    这道题和bzoj上一道叫魔法碰撞的题很像,只不过做法更加巧妙了. 一开始的想法是$f[i][j][k][0/1/2]$表示后i个数有j段当前混乱程度为k的方案,最后一维表示边界还能放几个. 转移的时候 ...

  2. springcloud与dubbo对比:

    我们直接将结论先列出来,然后逐个分析: 本博客借鉴此文章:http://blog.csdn.net/shuijieshuijie/article/details/53133082 打个不恰当的比喻: ...

  3. CGI浏览器与服务器的交互

    一直在做项目,跟着写前端后端,却没有思考一个问题:前端和后端为什么能够进行通信?为什么能够将HTML页面的内容传输给后台,然后又将结果反馈给前端? 寒假偶尔看到了这个问题,也解决了我的疑惑,这是基于C ...

  4. python中的requests使用小结

    现接触到的很少,详细的官方教程地址: requests官方指南文档:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html ...

  5. python常用模块-配置文档模块(configparser)

    python常用模块-配置文档模块(configparser) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. ConfigParser模块用于生成和修改常见配置文档,当前模块的名称 ...

  6. Solr记录-solr介绍及配置

    Solr是一个开源搜索平台,用于构建搜索应用程序. 它建立在Lucene(全文搜索引擎)之上. Solr是企业级的,快速的和高度可扩展的. 使用Solr构建的应用程序非常复杂,可提供高性能. 为了在C ...

  7. poj 3686 Priest John's Busiest Day

    http://poj.org/problem?id=3683 2-sat 问题判定,输出一组可行解 http://www.cnblogs.com/TheRoadToTheGold/p/8436948. ...

  8. python 玩具代码

    脚本语言的第一行,目的就是指出,你想要你的这个文件中的代码用什么可执行程序去运行它,就这么简单 #!/usr/bin/python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的python ...

  9. grep 正则表达

    常见的 grep 正则表达参数 -c # 显示匹配到得行的数目,不显示内容 -h # 不显示文件名 -i # 忽略大小写 -l # 只列出匹配行所在文件的文件名 -n # 在每一行中加上相对行号 -s ...

  10. BFS的队列

    按老师上课的话来总结,队列变化多端:   普通模板没有代价: 普通队列FIFO 01代价: 双端队列,单调队列 任意代价: 优先队列/堆,最短路SPFA/DIJKSTRA