用法Promise解决多层异步调用的简洁学习心得__第1页
用法Promise解决多层异步调用的简洁学习心得__第2页
用法Promise解决多层异步调用的简洁学习心得__第3页
用法Promise解决多层异步调用的简洁学习心得__第4页
用法Promise解决多层异步调用的简洁学习心得__第5页
免费预览已结束,剩余14页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、用法Promise解决多层异步调用的简洁学习心得_ 下面我就为大家带来一篇用法Promise解决多层异步调用的简洁学习心得。我觉得挺不错的,现在分享给大家,也给大家做个参考。 前言 第一次接触到Promise这个东西,是2021年微软发布Windows8操作系统后抱着作死奇怪的心态讨论用html5写Metro应用的时候。当时配合html5供应的WinJS库里面的异步接口全都是Promise形式,这对那时候刚刚毕业一点javascript基础都没有的我而言简直就是天书。我当时想的是,微软又在脑洞大开的瞎捣鼓了。 结果没想到,到了2021年,Promise居然写进ES6标准里面了。而且一项调查显示

2、,js程序员们用这玩意用的还挺high。 讽刺的是,作为早在2021年就在Metro应用开发接口里面广泛用法Promise的微软,其自家扫瞄器IE直到2021年寿终正寝了都还不支持Promise,看来微软不是没有这个技术,而是真的对IE放弃治疗了。 现在回想起来,当时看到Promise最头疼的,就是初学者看起来匪夷所思,也是最被js程序员广为称道的特性:then函数调用链。 then函数调用链,从其本质上而言,就是对多个异步过程的依次调用,本文就从这一点着手,对Promise这一特性进行讨论和学习。 Promise解决的问题 考虑如下场景,函数延时2秒之后打印一行日志,再延时3秒打印一行日志,

3、再延时4秒打印一行日志,这在其他的编程语言当中是特别简洁的事情,但是到了js里面就比较费劲,代码大约会写成下面的样子: var myfunc = function() setTimeout(function() console.log(log1); setTimeout(function() console.log(log2); setTimeout(function() console.log(log3); , 4000); , 3000); , 2021); 由于嵌套了多层回调结构,这里形成了一个典型的金字塔结构。假如业务规律再简单一些,就会变成令人闻风丧胆的回调地狱。 假如意识比较好,知

4、道提炼出简洁的函数,那么代码差不多是这个样子: var func1 = function() setTimeout(func2, 2021); ; var func2 = function() console.log(log1); setTimeout(func3, 3000); ; var func3 = function() console.log(log2); setTimeout(func4, 4000); ; var func4 = function() console.log(log3); ; 这样看起来略微好一点了,但是总觉得有点怪怪的。好吧,其实我js水平有限,说不上来为什么这

5、样写不好。假如你知道为什么这样写不太好所以创造了Promise,请告诉我。 现在让我们言归正传,说说Promise这个东西。 Promise的描述 这里请允许我引用MDN对Promise的描述: Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算.。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。 Promise 对象是一个返回值的代理,这个返回值在promise对象创建时未必已知。它允许你为异步操作的胜利或失败指定处理方法。 这使得异步方法可以像同步方法那样返回值:异步方法会返回一个包含了原返回值的 promise 对象来替代原

6、返回值。 Promise对象有以下几种状态: pending: 初始状态, 非 fulfilled 或 rejected。 fulfilled: 胜利的操作。 rejected: 失败的操作。 pending状态的promise对象既可转换为带着一个胜利值的fulfilled 状态,也可变为带着一个失败信息的 rejected 状态。当状态发生转换时,promise.then绑定的方法(函数句柄)就会被调用。(当绑定方法时,假如 promise对象已经处于 fulfilled 或 rejected 状态,那么相应的方法将会被立即调用, 所以在异步操作的完成状况和它的绑定方法之间不存在竞争条件。

7、) 更多关于Promise的描述和示例可以参考MDN的Promise条目,或者MSDN的Promise条目。 尝试用法Promise解决我们的问题 基于以上对Promise的了解,我们知道可以用法它来解决多层回调嵌套后的代码蠢笨难以维护的问题。关于Promise的语法和参数上面给出的两个链接已经说的很清晰了,这里不重复,挺直上代码。 我们先来尝试一个比较简洁的状况,只执行一次延时和回调: new Promise(function(res, rej) console.log(Date.now() + start setTimeout); setTimeout(res, 2021); ).then

8、(function() console.log(Date.now() + timeout call back); ); 看起来和MSDN里的示例也没什么区分,执行结果如下: $ node promisTest.js 1450194136374 start setTimeout 1450194138391 timeout call back 那么假如我们要再做一个延时呢,那么我可以这样写: new Promise(function(res, rej) console.log(Date.now() + start setTimeout 1); setTimeout(res, 2021); ).th

9、en(function() console.log(Date.now() + timeout 1 call back); new Promise(function(res, rej) console.log(Date.now() + start setTimeout 2); setTimeout(res, 3000); ).then(function() console.log(Date.now() + timeout 2 call back); ) ); 好像也能正确运行: $ node promisTest.js 1450194338710 start setTimeout 1 14501

10、94340720 timeout 1 call back 1450194340720 start setTimeout 2 1450194343722 timeout 2 call back 不过代码看起来蠢萌蠢萌的是不是,而且模糊又在搭金字塔了。这和引入Promise的目的南辕北辙。 那么问题出在哪呢?正确的姿态又是怎样的? 答案藏在then函数以及then函数的onFulfilled(或者叫onCompleted)回调函数的返回值里面。 首先明确的一点是,then函数会返回一个新的Promise变量,你可以再次调用这个新的Promise变量的then函数,像这样: new Promise(

11、.).then(.) .then(.).then(.).then(.). 而then函数返回的是什么样的Promies,取决于onFulfilled回调的返回值。 事实上,onFulfilled可以返回一个一般的变量,也可以是另一个Promise变量。 假如onFulfilled返回的是一个一般的值,那么then函数会返回一个默认的Promise变量。执行这个Promise的then函数会使Promise立刻被满足,执行onFulfilled函数,而这个onFulfilled的入参,即是上一个onFulfilled的返回值。 而假如onFulfilled返回的是一个Promise变量,那个这个

12、Promise变量就会作为then函数的返回值。 关于then函数和onFulfilled函数的返回值的这一系列设定,MDN和MSDN上的文档都没有明确的正面描述,至于ES6官方文档ECMAScript 2021 (6th Edition, ECMA-262)。我的水平有限实在看不懂,假如哪位高手能说明清晰官方文档里面对着两个返回值的描述,请肯定留言指教! 所以以上为我的自由发挥,语言组织的有点拗口,上代码看一下大家就明白了。 首先是返回一般变量的状况: new Promise(function(res, rej) console.log(Date.now() + start setTimeo

13、ut 1); setTimeout(res, 2021); ).then(function() console.log(Date.now() + timeout 1 call back); return 1024; ).then(function(arg) console.log(Date.now() + last onFulfilled return + arg); ); 以上代码执行结果为: $ node promisTest.js 1450277122125 start setTimeout 1 1450277124129 timeout 1 call back 145027712412

14、9 last onFulfilled return 1024 有点意思对不对,但这不是关键。关键是onFulfilled函数返回一个Promise变量可以使我们很便利的连续调用多个异步过程。比如我们可以这样来尝试连续做两个延时操作: new Promise(function(res, rej) console.log(Date.now() + start setTimeout 1); setTimeout(res, 2021); ).then(function() console.log(Date.now() + timeout 1 call back); return new Promise

15、(function(res, rej) console.log(Date.now() + start setTimeout 2); setTimeout(res, 3000); ); ).then(function() console.log(Date.now() + timeout 2 call back); ); 执行结果如下: $ node promisTest.js 1450277510275 start setTimeout 1 1450277512276 timeout 1 call back 1450277512276 start setTimeout 2/p>

16、27 timeout 2 call back 假如觉得这也没什么了不得,那再多来几次也不在话下: new Promise(function(res, rej) console.log(Date.now() + start setTimeout 1); setTimeout(res, 2021); ).then(function() console.log(Date.now() + timeout 1 call back); return new Promise(function(res, rej) console.log(Date.now() + start setTimeout 2); se

17、tTimeout(res, 3000); ); ).then(function() console.log(Date.now() + timeout 2 call back); return new Promise(function(res, rej) console.log(Date.now() + start setTimeout 3); setTimeout(res, 4000); ); ).then(function() console.log(Date.now() + timeout 3 call back); return new Promise(function(res, rej

18、) console.log(Date.now() + start setTimeout 4); setTimeout(res, 5000); ); ).then(function() console.log(Date.now() + timeout 4 call back); ); $ node promisTest.js 1450277902714 start setTimeout 1 1450277904722 timeout 1 call back 1450277904724 start setTimeout 2 1450277907725 timeout 2 call back 145

19、0277907725 start setTimeout 3 1450277911730 timeout 3 call back 1450277911730 start setTimeout 4 1450277916744 timeout 4 call back 可以看到,多个延时的回调函数被有序的排列下来,并没有消失喜闻乐见的金字塔状结构。虽然代码里面调用的都是异步过程,但是看起来就像是全部由同步过程构成的一样。这就是Promise带给我们的好处。 假如你有把啰嗦的代码提炼成单独函数的好习惯,那就更加画美不看了: function timeout1() return new Promise(f

20、unction(res, rej) console.log(Date.now() + start timeout1); setTimeout(res, 2021); ); function timeout2() return new Promise(function(res, rej) console.log(Date.now() + start timeout2); setTimeout(res, 3000); ); function timeout3() return new Promise(function(res, rej) console.log(Date.now() + start

21、 timeout3); setTimeout(res, 4000); ); function timeout4() return new Promise(function(res, rej) console.log(Date.now() + start timeout4); setTimeout(res, 5000); ); timeout1() .then(timeout2) .then(timeout3) .then(timeout4) .then(function() console.log(Date.now() + timout4 callback); ); $ node promis

22、Test.js 1450278983342 start timeout1 1450278985343 start timeout2 1450278988351 start timeout3 1450278992356 start timeout4 1450278997370 timout4 callback 接下来我们可以再连续讨论一下onFulfilled函数传入入参的问题。 我们已经知道,假如上一个onFulfilled函数返回了一个一般的值,那么这个值为作为这个onFulfilled函数的入参;那么假如上一个onFulfilled返回了一个Promise变量,这个onFulfilled的

23、入参又来自哪里? 答案是,这个onFulfilled函数的入参,是上一个Promise中调用resolve函数时传入的值。 跳动的有点大一时间无法接受对不对,让我们来好好缕一缕。 首先,Promise.resolve这个函数是什么,用MDN上面文邹邹的说法 用胜利值value解决一个Promise对象。假如该value为可连续的(thenable,即带有then方法),返回的Promise对象会“跟随”这个value 简而言之,这就是异步调用胜利状况下的回调。 我们来看看一般的异步接口中,胜利状况的回调是什么样的,就拿nodejs的上的fs.readFile(file, options, ca

24、llback)来说,它的典型调用例子如下 fs.readFile(/etc/passwd, function (err, data) if (err) throw err; console.log(data); ); 由于对于fs.readFile这个函数而言,无论胜利还是失败,它都会调用callback这个回调函数,所以这个回调接受两个入参,即失败时的特别描述err和胜利时的返回结果data。 那么假如我们用Promise来重构这个读取文件的例子,我们应当怎么写呢? 首先是封装fs.readFile函数: function readFile(fileName) return new Prom

25、ise(function(resolve, reject) fs.readFile(fileName, function (err, data) if (err) reject(err); else resolve(data); ); ); 其次是调用: readFile(theFile.txt).then( function(data) console.log(data); , function(err) throw err; ); 想象一下,在其他语言的读取文件的同步调用接口的里面,文件的内容通常是放在哪里?函数返回值对不对!答案出来了,这个resolve的入参是什么?就是异步调用胜利状况

26、下的返回值。 有了这个概念之后,我们就不难理解“onFulfilled函数的入参,是上一个Promise中调用resolve函数时传入的值”这件事了。由于onFulfilled的任务,就是对上一个异步调用胜利后的结果做处理的。 哎最终理顺了。 总结 下面请允许我用一段代码对本文讲解到的要点进行总结: function callp1() console.log(Date.now() + start callp1); return new Promise(function(res, rej) setTimeout(res, 2021); ); function callp2() console.log(Date.now() + start callp2); return new Prom

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论