c++ 异步下获取线程执行结果_前端异步编程的那些事

39a18ef4d54bc3b701e59cfa666f1382.gif

e00d39b65247451a91782791f2fdbcd7.png

    

一、异步编程的运行机制

    我们学习Javascript语言的时候就知道它的执行环境是”单线程“的。

    所谓”单线程“,就是指一次只能处理一个任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。  

    所以为了解决“单线程”引发的出来的问题,就有了前端异步编程的这个解决方案,下面我们就来探讨一下浏览器的异步编程是一个怎么的运行机制。

1.1异步任务

    前端中常见的异步任务有以下:

•    回调函数

•     事件绑定

•     定时器(settimeout ,setinterval)

•     ajax

•     Promise

    是异步的吗?我们来验证下:

new Promise((resolve,reject)=>{  console.log(1);  resolve();}).then(()=>{  console.log(2);})console.log(3);

    我们看下控制台的输出顺序:

8e1935f5db7c1c6ce24b68fc72bc722b.png

    由上可见 new Promise 的时候传递的executor函数是立即执行的(同步),基于then、catch存放的方法是异步执行的。

•     async/await

    这是基于Generator语法糖,同样也是异步的,我们把上面的例子改成这样看看。

async function async1() {console.log(1);await 10;console.log(2);}fn();console.log(3);

我们看下控制台的输出顺序:

8e1935f5db7c1c6ce24b68fc72bc722b.png

由上可见 await 表达式后面的代码是异步执行的 。

1.2Event Loop

    那么JS是如何构建出异步编程的效果呢?那我们就来了解下事件循环机制(Event Loop )与事件队列(Event Queue)。

cd602300c9ba9aa5e9da276fd22cf0f1.png

1、开始,任务执行。

2、同步任务直接在主栈(Call Stack)中等待被执行,异步的进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。

3、当 Call Stack中没有任务了,就从Event Queue中拿出一个任务放入Call Stack执行。

而 Event Loop 指的就是这一整个圈圈:它不停检查 Call Stack 中是否有任务(也叫栈帧)需要执行,如果没有,就检查 Event Queue,从中弹出一个任务,放入主栈中,如此往复循环。

我们来看个例子: 

setTimeout(()=>{  console.log(1);},50)setTimeout(()=>{  console.log(2);},0)console.time('for takes time:');for(let i=0;i<1000000;i++){}console.timeEnd('for takes time:');setTimeout(()=>{       console.log(3);},20)console.log(4); 

我们先看下执行结果再来分析。

e7634c6dcce2a9edf497d34b77643a20.png

代码分析如下:

a8a0410b904794dc24c716ae1f248ea1.png

    这里主要容易疑惑点的点是T1先执行还是T3先执行,这里要看for 循环主要消耗的时间,for 循环用了5ms(假设是个整数),这时候队列中T1需要等待的时间还有45ms,所以执行完T2后,等待到了20ms 就会执行T3,最后才执行T1。如果把for循环耗的时间是100ms,那么执行顺序就是T2=>T1=>T3,遵守时间到先执行,先进队列先执行的原则。

    我们来看个稍微复杂点的例子:

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');})async1();new Promise(function(resolve){  console.log('promise1');  resolve();}).then(function(){  console.log('promise2');})console.log('script end');

    学习上面的Event Loop ,同步任务先执行,再遵守先进先出的原则从Event Queue中取出异步任务到主栈中执行,我自信的以为输出顺序为:

'script start'  'async1 start'  'async2'  'promise1'    'script end'  'setTimeout'  'async1 end'   'promise2'   

然后迫不及待的去控制台执行下:

d7af7c5ecc57e7b4c63b190ff171de1e.png

2093324ecd1427204aaa3d2efbd4d04f.gif

为什么顺序执行是这样子的?于是继续查资料。

1.3宏任务与微任务

Javacript中事件执行主要分为两种任务类型:宏任务(macro task)与微任务队列(micro task)。

•     宏任务包含有 :

script(整体代码)setTimeoutsetIntervalI/OUI交互事件postMessageMessageChannelsetImmediate(Node.js 环境)

     微任务包含有 :

Promise.then/catch/finaly,  async/await(本质是对Promise 的一些封装)Object.observeMutaionObserverprocess.nextTick(Node.js 环境)

1.4宏任务与微任务运行机制

1、执行一个宏任务(栈中没有就从Event Queue中取出)

2、遇到异步宏任务时它加入到Event Queue宏任务队列中;遇到异步微任务时把它加入Event Queue微任务队列中。

3、宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)

4、当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染

5、渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

如下图:

0e953b93acb3b665a5449db3d4cf3f59.png

所以上面例子(代码5)的执行分析如下图所示:

31d1a215300036c2fb3fb980deaba8ff.png

d6f1bbfbb33178cd606d495a79d6d47c.gif

1.5总结以上的内容

•     javascript是一门单线程语言

•     javascript事件循环就是一个Event Loop 过程

•     Event Queue 中的又分为宏任务队列与微任务队列

•     Javscript代码的执行顺序:同步任务=>异步微任务=>异步宏任务

二、异步编程使用场景

    上面内容我们探讨了浏览器异步编程是的运行机制。我们一般会在哪些场景使用异步编程呢?下面我们看个例子:

a9938581924ffc194d569c33887862ec.png

    现在有个业务需求就是要先请求接口1拿到“系统”的下拉数据后,并且要给表单赋值为下拉数据的第一个,拿到“系统”的value值后再请求接口2取到“表单分类”的下拉数据,并且要给表单赋值,拿到“表单分类“的值后再请求接口3,取表单的下拉数据。
我们传统的做法一般会用回调函数:

//获取系统_self.ajaxFn({  url:url1,  success:(res)=>{    //获取表单分类    _self.ajaxFn({        url:url2,        success:(res)=>{           //获取表单           _self.ajaxFn({              url:url3,              success:(res)=>{                //处理业务              }           })        }    })  }})

2.1回调地狱

    回调函数是异步编程的一个传统解决方案,但是如果业务比较复杂,需要层层嵌套,就会引起回调地狱。

66e474f4f674bff8156f7bcbfe3251f4.png

这样子的代码嵌套复杂,不易于维护 ,着实让开发者崩溃 。

431c34dc7e2d2dbcda1a5c1e06647c28.png

三、Promise原理及实现

3.1Promise原理及实现

    Promise是异步编程的一个解决方案,相比回调函数,使用Promise更为合理和强大,避免了回调函数之间的多嵌套,也使得代码结构更为清晰,便于维护。上面的例子我们用Promise来实现下。

new Promise((resolve,reject)=>{    //获取系统    _self.ajaxFn({        url:url1,        success:(res)=>{          resolve(res);        }    })}).then(   value=>{    return new Promise((resolve,reject)=>{      //获取表单分类      _self.ajaxFn({          url:url2,          success:(res)=>{            resolve(res);          }      })    })   }).then(   value=>{      //获取表单      _self.ajaxFn({          url:url1,          success:(res)=>{            //处理逻辑          }      })   })

接下来我们就要分析Promise是原理并且要自己实现Promise。

3.2 promise自定义实现的关键点

1、如何改变promise的状态?

1.1 resolve(value);状态从pending 转为了 resolved

1.2 rejecte(reason);状态从pending 转为了 rejected

1.3 抛出异常(throw);当前的pending 就变为了 rejected

2、Promise then/catch 存放的回调函数是异步微任务的 。

3、Promise.then()返回的新的Promise的结果状态由什么决定的呢?

3.1 简单描述:由then指定的回调函数执行的结果决定

3.2 详细描述:

3.2.1 如果抛出异常,新Promise变为rejected, reason 为抛出的异常;

3.2.2 如果返回的是Promise的任意值,新promise变为resolved,value 为返回的值

3.2.3 如果返回的是另一个Promise,此Promise的结果成为新Promise的结果

4、Promise.then()/catch()可链式调用,所以then(),catch方法必须返回一个的Promise对象。

3.3 自定义Promise

1、定义整体结构

(function(){    /*     构造函数     excutor:执行器(同步执行)    */    function Promise(excutor){}    /*      Promise 原型对象的then()      指定成功和失败的回调函数      因为可链式调用,所以要返回一个promise对象    */    Promise.prototype.then=function(onResolved,onRejected){ }    /*      Promise 原型对象的catch()      指定失败的回调函数      返回一个promise对象    */    Promise.prototype.catch=function(onRejected){ }    /*     只实现Promise关键 的构造方法以及catch/then方法,其它的方法大家可自行发挥啦    */        window.Promise=Promise;})(window)

2、Promise中回调的函数的异步执行

    第一部分内容我们就知道Promise.then/catch中存放是任务是方式是异步执行,且是微任务。异步执行我们很容易就能想到setTimeout能实现,但是setTimeout是宏任务,所以不符合,于是我就找到了Vue中的nextTick的方法来模拟一下,它主要是用HTML5新的API MutationObserver(不熟悉的可自行了解下)来实现的,第一部分内容我们有说到MutationObserver是微任务。

const nextTick = (function () {  var callbacks = [];  var pending = false;  var timerFunc;  function nextTickHandler () {      pending = false;      var copies = callbacks.slice(0);      callbacks = [];      for (var i = 0; i < copies.length; i++) {        copies[i]()      }  }   //istanbul ignore if   if (typeof MutationObserver != 'undefined') { // 首选 MutationObserver     var counter = 1    var observer = new MutationObserver(nextTickHandler) // 声明 MO 和回调函数    var textNode = document.createTextNode(counter)    observer.observe(textNode, { // 监听 textNode 这个文本节点      characterData: true // 一旦文本改变则触发回调函数 nextTickHandler    })    timerFunc = function () {      counter = (counter + 1) % 2 // 每次执行 timeFunc 都会让文本在 1 和 0 间切换      textNode.data = counter    }  } else {    timerFunc = setTimeout // 如果不支持 MutationObserver, 退选 setTimeout  }  return function (cb, ctx) {    var func = ctx ? function () { cb.call(ctx) } : cb    callbacks.push(func)    if (pending) return    pending = true    timerFunc(nextTickHandler, 0)  }})()

3、Promise 的构造函数的实现

/* 构造函数 excutor:执行器*/function Promise(excutor){  const _self=this;  _self.status='pending';//promise对象指定的status属性,初始值为pending;  _self.data=undefined;//promise对象指定一个用于存储结果数据的属性;  _self.callbacks=[];//失败或成功的回调函数数组,每个元素的结构:{onResolved(){},onRejected(){}}  function resolve(value){      if(_self.status!='pending'){      return;    }    _self.status='resolved'; //将状态改为resolved    _self.data=value; // 保存value    //如果有待将执行的回调函数,就立即异步执行回调函数 onResolved    nextTick(()=>{      _self.callbacks.forEach((callbacksObj)=>{        callbacksObj.onResolved(value);      })    })  }  function reject(reason){    if(_self.status!='pending'){      return;    }    _self.status='rejected'; //将状态改为rejected    _self.data=reason; // 保存value    //如果有待将执行的回调函数,就立即异步执行回调函数 onRejected    nextTick(()=>{      _self.callbacks.forEach((callbacksObj)=>{        callbacksObj.onRejected(reason);      })    })  }  //立即执行执行器  try{    excutor(resolve,reject);  }  catch(error){    reject(error);  }  }

4、Promise.prototype.then()方法的实现

/*  Promise 原型对象的then()方法  指定成功和失败的回调函数  因为可链式调用,所以必须要要返回一个promise对象  返回的promise 的结果是由onResolved/onRejected执行的结果决定的*/Promise.prototype.then=function(onResolved,onRejected){  var _self=this;  //指定回调函数的默认值,如果参数不是函数就指定个默认方法  onResolved=typeof onResolved==='function'? onResolved :value => value;    onRejected=typeof onRejected==='function'? onRejected :reason => {throw reason};  return new Promise((resolve,reject)=>{    /*       执行指定callback(onResolved/onRejected)的回调函数       根据执行的结果改变return的pormise的状态    */    function handle(callback){        try{        const result=callback(_self.data);         if(result instanceof Promise){ //返回的是promise,返回promise的结果就是这个结果                    result.then(resolve,reject)        }else{//返回的不是promise,返回的promise为成功,value就是返回值          resolve(result)          }      }catch(error){ //抛出异常,返回promise的结果为失败,reason为异常        reject(error)      }    }    if(_self.status==='resolved'){ //当前promise的状态是resolved      //异步执行成功的回调      nextTick(()=>{        handle(onResolved)      })    }else if(_self.status==='rejected'){//当前promise的状态是rejected      //异步执行失败的回调      nextTick(()=>{        handle(onRejected)      })    }else{ //当前promise 的状态是pending      //将成功和失败的回调函数保存到callback中去存起来      _self.callbacks.push({        onResolved(){          handle(onResolved);        },        onRejected(){          handle(onRejected);        }      })    }  })

5、Promise.prototype.catch()方法的实现

/*  Promise 原型对象的catch()  指定失败的回调函数  返回一个promise对象*/Promise.prototype.catch=function(onRejected){  return this.then(null,onRejected)}

    大功告成,其实主要是把then 方法实现,其它的都好实现了,其它的方法大家试下吧。

39a18ef4d54bc3b701e59cfa666f1382.gif

四、最后

    这篇文章主要跟大家探讨了浏览器异步编程的运行机制还有Promise的实现原理。

    前端之路漫漫其修远兮,吾将上下而求索,与君共勉!06667120783b80f618402b95fb0c57fe.png

—— E N D ——

排版:chuanrui

15e10f3fdecfa3f6cd98e5127af470a1.gif

4a984a305d70607b901b8637ee79d85f.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/297553.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自动驾驶规划算法

本文将讲解BFS&#xff0c;Dijstra&#xff0c;A*&#xff0c;动态规划的算法原理&#xff0c;不正之处望读者指正&#xff0c;希望有兴趣的读者能在评论区提出一些这些算法的面试考点&#xff0c;共同学习&#xff0c;一起进步 0 图论基础 图有三种&#xff1a;无向图、有向…

印度网民集体删除中国APP,网友评论亮了

全世界只有3.14 % 的人关注了爆炸吧知识最近&#xff0c;国外有一款Remove China Apps火了。这款APP只有一个功能&#xff1a;可以一键扫描用户手机里的应用&#xff0c;识别出自中国公司的应用&#xff0c;并进行一键删除的骚操作。应用的主图标、界面&#xff0c;甚至特意使用…

SQL Server 负载均衡集群(转)

SQL Server 负载均衡集群一个应用系统随着业务量的提高,以及访问量和数据流量的快速增长&#xff0c;各个核 心部分的处理性能和计算强度也相应增大&#xff0c;使得单一设备根本无法承担。在此情况下&#xff0c;如果扔掉现有设备去做大量的硬件升级&#xff0c;必将造成现有资…

大前端快闪二:react开发模式 一键启动多个服务

最近全权负责了一个前后端分离的web项目&#xff0c;前端使用create-react-app[1], 后端使用golang做的api服务。npx create-react-app my-app cd my-app npm start歘歘歘&#xff0c;就搭建了一个react前端项目。前端老鸟都知道npm start或yarn start以开发模式启动react App&…

数据库比较工具SQL Delta

SQL Delta 版本4只支持SQL Server数据库 版本5支持Oracle 但是数据库中不可以有英文 否则连接时会报 ORA-12737: Instant Client Light: unsupported server character set ZHS16GBK的错误 配置如下所示&#xff1a; 转载于:https://blog.51cto.com/shuimomo/461266

女朋友在家是怎么利用我的模型的​

1 趁着奶猫还没长大&#xff0c;赶紧摸2 说有谁不喜欢玩具呢3 爸爸再也不用费心编辫子了4 谁知道这是什么5 还挺会利用的啊6 学霸上厕所的时候7 逻辑推理你点的每个赞&#xff0c;我都认真当成了喜欢

abap al设置单元格可编辑 oo_润乾报表美化设置 -- 样式

在制作报表时&#xff0c;报表设计人员经常遇到下面这些美化报表的问题&#xff1a;为什么我做出的报表领导总觉得不好看不满意 美化一张报表要设置太多的格式和属性&#xff0c;那么多报表都得重复设置&#xff0c;真繁琐 系统中不同人做出的报表展现格式各不相同&#xff0c;…

GRPC在网页前端的使用

一直以来都是一个桌面端程序狗&#xff0c;某天突发奇想&#xff0c;想用网页实现一个客户端&#xff0c;于是开始了electronvueasp.netcore的探寻之路&#xff0c;这条道路很坎坷主要是css让我头疼不已&#xff08;当然我们并没有放弃使用wpf&#xff0c;我是一个wpf老手&…

vue 带全选和多选的表格怎么写_EXCEL五分钟,批量制作带照片的工地出入证

作者&#xff1a;祝洪忠 转自&#xff1a;Excel之家ExcelHome小伙伴们好啊&#xff0c;今天和大家分享一个邮件合并的技巧——批量制作带照片的工地出入证。先看效果图&#xff1a;要准备的材料包括&#xff1a;一、Excel资料表资料表中包括姓名、工号和用姓名命名的带格式后缀…

从小一看到数字,脑子里就开始搞颜色......

1 脑子里的数字有颜色估计都是它的锅▼2 不愧是你▼3 文科生考场惯用伎俩填满了就是胜利▼4 每年向社会输送大量精英人才▼5 &#xff1f;&#xff1f;&#xff1f;▼6 高考最后一晚逆袭的答案在一本叫《刑法》的书里▼7 中国外卖VS俄罗斯外卖毛子送外卖坐公交你敢信&am…

android键盘弹出,聊天背景不变形

2019独角兽企业重金招聘Python工程师标准>>> android:transcriptMode"normal"&#xff0c;如果软键盘弹出时候&#xff0c;设置该属性listview会自动调整到最后&#xff0c;这样弹出的软键盘就不会遮盖到listview了 背景图尽量用 getWindow().setBackgrou…

bpmn文件的标签为何都以bpmn2开头_C语言之两种作用域:函数的作用域与文件作用域...

作用域描述程序中可访问标识符的区域。一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域或文件作用域。其中&#xff0c;块是用一对花括号括起来的代码区域。例如&#xff0c;整个函数体是一个块&#xff0c;函数中的任意复合语句也是一个块。定义在块中的变量具有…

集成开放平台标准化连接器之基于OAS3.0的API管理能力

源宝导读&#xff1a;随着企业信息化进程的逐步深入&#xff0c;互联网技术的发展和分布式系统应用的日益广泛&#xff0c;直接导致大量异构系统的存在&#xff0c;这些系统往往各自独立、封闭运行&#xff0c;相互之间不存在或很少存在数据的交互&#xff0c;由于这种应用分割…

axure 组件_技巧分享 | Axure后台组件制作的全过程

点击上方蓝字&#xff0c;关注行设视觉黑马家族成员原创发布于行设视觉&#xff0c;转载请注明出处。作者 | 时光若刻http://www.woshipm.com/rp/2527010.html大家好&#xff0c;前一段时间我刚刚分享了一篇《打造高品质Axure组件库就是这么简单》&#xff0c;意在分享给大家一…

有没有测试人心里的软件,心理测评,认识自己的有效工具:(二)你做的可能是假的心理测试...

上一篇文章主要介绍了心理测评的基础知识&#xff0c;今天在上一篇的基础上&#xff0c;介绍下网上一些随处可见的心理测评&#xff0c;是如何的错误&#xff0c;以及怎么“迷惑”大家的&#xff1f;网上随便搜的一个阅读量超过10w的"心理"测试&#xff1a;测试他人会…

这相册一出手,哪个长辈搞不定?

1 这相册看着多喜庆看这花多美▼2 不会只有我一个人连最普通的都叠不好吧&#xff1f;▼3 真就史上最惨毕业生连毕业照都不配拥有吗&#xff1f;▼4 看起来经历了很严格的军训▼5 我闺蜜【偷】男票的钱给我买MAC air▼6 在台湾爸比和东北妈妈中间无缝切换▼7 先漱口、后…

医疗大数据:商业保险、移动医疗的崛起,正在形成闭环(二)

以下内容摘自华创证券分析师 吴晓雯、张伟光的《挖掘医疗大数据中的金矿》报告。 接上篇&#xff1a;医疗大数据&#xff1a;商业保险、移动医疗的崛起&#xff0c;正在形成闭环&#xff08;一&#xff09; 三、 商业保险、移动医疗等商业力量的崛起&#xff0c;加速医疗大数据…

WriteComponent,ReadComponent

WriteComponent,ReadComponent 代码 varForm1: TForm1; ms: TMemoryStream;implementation{$R *.dfm}procedureTForm1.Button1Click(Sender: TObject);varaBtn: TButton;beginaBtn :TButton.Create(Self); aBtn.Parent :Self; aBtn.Caption :Test; aBtn.Left :10; ms.WriteComp…

jquery 使用方法

jQuery是目前使用最广泛的javascript函数库。据统计&#xff0c;全世界排名前100万的网站&#xff0c;有46%使用jQuery&#xff0c;远远超过其他库。微软公司甚至把jQuery作为他们的官方库。对于网页开发者来说&#xff0c;学会jQuery是必要的。因为它让你了解业界最通用的技术…

bread是可数还是不可数_雅思官方语法教程之——这个名词到底可不可数?

名词是否可数&#xff0c;是非常隐形的扣分点。考生很可能意识不到自己犯错了&#xff0c;毕竟这是中文里不存在的语法点。这类语法点更应该重点备考。越练习&#xff0c;越熟悉&#xff0c;犯错扣分的几率也就越低。OK进入主题↓语法解释官方对可数和不可数名词的解释如下&…