撩课-Web架构师养成系列(第二篇)-async
2018-12-09 11:19:18来源:博客园 阅读 ()
前言
Web架构师养成系列共15篇,每周更新一篇,主要分享、探讨目前大前端领域(前端、后端、移动端)企业中正在用的各种成熟的、新的技术。部分文章也会分析一些框架的底层实现,让我们做到知其然知其所以然。
本篇为第二篇,上一篇:撩课-Web架构师养成系列第一篇
本篇文章阅读需要时长:约15分钟
一、先了解异步?
关于"异步",我们可以这么理解: 一个任务拆分成两段,先执行第一段,然后转而执行其他任务,等到某个时间点,再回过头执行第二段。
比如,你要做土豆炖牛肉,当开始煮牛肉的时候发现土豆没了,可以先让牛肉煮着,然后去买土豆、洗好、切好,再把土豆放到锅里一起煮。
这种不连续的执行,就叫做异步。相应地,如果是连续的执行,那么就叫做同步。
1.1 JS中常见的异步编程方式?
异步编程的目标就是让代码的执行更加类似于同步编程,开发中比较常用的方式主要包括: 1) 回调函数实现 2) 发布订阅、通知 3) 事件监听 4)Promise/A+ 和 生成器函数 5)async/await 在ES6之前,我们更多地是使用回调函数来实现异步编程。
1.2 认识回调函数
回调函数就是把任务拆解成两部分,把任务的第二部份单独写在一个函数里面,等到执行完其它任务重新执行这个任务的时候,就直接调用这个函数,从而达到异步效果。 案例如下: /** * 土豆炖牛肉 * @param step1 牛肉 * @param callback 回调 */ let cook = (step1, callback) => { // 1. 煮牛肉 console.log(`烧水煮${step1}`); // 2. 放入土豆(5秒后执行) setTimeout(() => { let step2 = '放入土豆'; callback(step2); }, 5000) }; // 1. 先煮牛肉 cook('牛肉', (data) => { console.log(data); }); // 2. 做其它事, 5s后放入土豆 console.log('买土豆'); console.log('洗土豆'); console.log('切土豆');
二、异步改进方案-Promise
2.1 什么是Promise?
promise,承诺。在代码中我们可以这么理解:此处我先许下个承诺,过了一定时间后我带给你一个结果。
那么,在这一段时间中做什么?
我们可以进行异步操作,比如请求网络数据、耗时运算、读写本地文件等
2.2 Promise的三种状态?
1) Pending Promise对象实例创建时候的初始状态 2) Fulfilled 成功的状态 3) Rejected 失败的状态 比如:你发邮件给老板说要加工资,这时候你就要"等待"他的邮件回复,他可以立马给你回复,如果同意了,表示"成功";如果不同意,表示"失败",当然他也可以一直不同意;但是这期间不影响你做其它事情。 在实际开发中,我们可以通过then 方法,来指定Promise 对象的状态改变时确定执行的操作,resolve 时执行第一个函数(onFulfilled),reject 时执行第二个函数(onRejected)。 来,一起认识下promise的几种操作方式和常用方法: 构建Promise // promise的方法会立刻执行; // 两个输出都会打印 let promise = new Promise(() => { console.log('喜欢IT'); }); console.log('就上撩课'); promise也可以代表未来的一个值 一个promise实例可以多次调用then,当成功后会将结果依次执行。 let promise = new Promise((resolve, reject) => { ajax.get(BASEURL + 'api/goods/', (err, data)=>{ if (err) return reject(err); resolve(data); }) }); promise.then(data => { console.log(data); }); promise.then(data => { console.log(data); }); promise也可以代表一个不用返回的值 // 代表一个用于不会返回的值 let promise = new Promise((resolve, reject) => { }); promise.then(data => { console.log(data); }); Promise.resolve 返回一个Promise实例,这个实例处于resolve状态。 Promise.resolve('成功获取结果').then(data=>{ console.log(data); }); Promise.reject 返回一个Promise实例,这个实例处于reject状态。 Promise.reject('获取结果失败').then(data=>{ console.log(data); },err=>{ console.log(err); }) Promise.race 该方法用于接收一个数组,数组内都是Promise实例,返回一个Promise实例,这个Promise实例的状态转移取决于参数的Promise实例的状态变化。 当参数中任何一个实例处于resolve状态时,返回的Promise实例会变为resolve状态。如果参数中任意一个实例处于reject状态,返回的Promise实例变为reject状态。 Promise.race( [readFiles('./a.txt'), readFiles('./b.txt')]).then(data=>{ console.log({data}) },(err)=>{ console.log(err) });
2.3 案例实操
我们再用promise实现上面发邮件加工资的案例:
情况一 :
在一定时间后(假设5s后),老板回复了邮件,可以是以下两种情况: let addWages = ()=>{ return new Promise((resolve, reject) => { setTimeout(function () { // 公司账户余额 let currentMoney = 9999999999; // 公司账户余额 > 100w if (currentMoney > 1000000) { resolve('同意加薪'); } else { resolve('不同意加薪'); } }, 5000) }) }; addWages().then(data => { console.log(data); }, data => { console.log(data); }); // 运行结果:同意加薪 情况二 : 公司账户已经没钱,没法加工资了,表现形式如下: let addWages = ()=>{ return new Promise((resolve, reject) => { throw new Error('你表现不够优秀!'); }) }; addWages().then(data => { console.log(data); }, data => { console.log('这里输出:' + data); }); 我们可以采用then的第二个参数捕获reject返回结果或者捕获失败,当然也可以通过.catch函数进行捕获。
三、promise可以解决回调函数带来的问题
前面的案例描述已经验证了promise支持catch,此外,通过promise也能够返回结果给外部。我们再一起看看promise如何解决回调地狱和同步异步结果。
3.1 解决回调地狱
案例场景:在文档a.txt中存放正文档b.txt的路径,在文档b.txt中存放正文档c.txt的路径, 我们要取出文档c.txt里面的内容。 构造函数实现: /* a.txt -> b.txt -> c.txt -> 输出内容*/ let fs = require('fs'); let readFiles = ()=>{ // 回调1 fs.readFile('./a.txt','utf8', (err,data)=>{ if(err) return console.log(err); // 回调2 fs.readFile(data,'utf8',function(err,data){ if(err) return console.log(err); // 回调3 fs.readFile(data,'utf8',function(err,data){ if(err) return console.log(err); console.log(data); }) }) }) }; /* 调用输出结果: 喜欢IT, 就上撩课(itlike.com) */ readFiles(); 通过promise解决回调地狱: let fs = require('fs'); // 1. 初始化promise let readFiles =(filePath)=>{ return new Promise((resolve,reject)=>{ fs.readFile(filePath,'utf8',(err,data)=>{ if(err) return reject(err); resolve(data); }) }) }; // 2. 类似于链式的调用方式 readFiles('./a.txt').then((data)=>{ return readFile(data); }).then((data)=>{ // 获取b.txt中内容 return readFile(data); }).then((data)=>{ // 输出c.txt中内容 console.log(data) }).catch((err)=>{ console.log(err) }); 3.2 在同一时刻同步所有异步产生的结果 该场景在实际开发中有很多应用场景,比如:我们要提交一个操作时,需要结合之前的两个异步请求的结果才能进行。 再比如:你要进行下一个运算时,需要前面两个异步运算的结果才能进行。我们还是通过读取文件的案例来进行举例。 常规方式实现: let fs = require('fs'); // 1. 统一输出所有异步产生的结果 let allContent = {}; let logAllContent = (key,data)=>{ allContent[key] = data; if(Object.keys(allContent).length === 2){ console.log(allContent) } }; // 2. 分别异步读取文件中的内容 fs.readFile('./a.txt', 'utf8', function (err, data) { if (err) return console.log(err); logAllContent(data); }); fs.readFile('./b.txt', 'utf8', function (err, data) { if (err) return console.log(err); logAllContent(data); }); 这样的方式虽然解决了问题,但是你不知道最终结果是在哪个异步函数中输出,而且你需要在所有的异步函数中都去调用打印方法。 promise方式大大简化: 借助promise.all()方法,不管哪个promise谁先完成,该方法会按照数组里面的顺序将结果返回。 let fs = require('fs'); let readFiles = (filePath)=>{ return new Promise(function(resolve,reject){ fs.readFile(filePath,'utf8', (err,data)=>{ if(err) return reject(err); resolve(data); }) }) }; Promise.all( [readFiles('./a.txt'), readFiles('./b.txt')] ).then(([data])=>{ console.log({data}) });
后续
借助Promise已经可以帮助我们很好解决异步编程的问题,但还不是那么的行云流水、一气呵成,我们更希望编写异步代码能够像写同步代码一样直观、简单。
在下一篇我们会讲些更好、更灵活的异步编程方案,敬请期待。获取资料、交流可加我微信:yejh9522 一起探讨学习。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:深入解析Vue文件树组件使用
- image-webpack-loader包安装报错解决 2019-08-14
- 用原生JS从零到一实现Redux架构 2019-04-28
- 撩课-Web大前端每天5道面试题-Day36 2019-01-21
- 撩课-Web大前端每天5道面试题-Day35 2019-01-15
- 撩课-Web大前端每天5道面试题-Day33 2019-01-15
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash