入职百度EFE团队实习已经三周了,实习中接触到了生产环境的技术和开发流程,大开眼界,和自己在学校接小作坊式项目是很不一样的体验。其中一个很大的感触是,ES6早已不是“选修”的尝鲜技术,而是已经全面普及应用、非学不可的“必修”技术,看来该版本新加入的特性的确解决了前端项目开发中的痛点,有利于提升开发效率。
说到ES6,像let,const,箭头函数这些特性简单易懂,很快就实现了无痛上手;然而Promise不是那么直观,项目文档里大量的“返回一个Promise对象”让初接触这一概念的我一度懵比,于是花了些脑筋去学习它,并感觉值得写一篇博客来谈一下我对它的理解。
我们知道,在ES5中,调用异步操作的方法如ajax、查询数据库等时,往往需要传一个回调函数来响应异步的返回结果,造成嵌套,而如果在回调函数里又有异步操作,就会造成更深的嵌套。。嵌套层级过深会使得代码可读性很差,对象字面量的花括号与函数的花括号齐飞,this也难以判断其运行时指向,造成“回调金字塔”现象,十分不利于开发和维护。在这种形势下,Promise应运而生,可谓众望所归。
Promise到底是什么?它是一个对象,正如其字面意思所示,它代表一个异步操作的“承诺”:这事包在我身上,虽然不知道啥时候能办好,但我会负责到底!
当我们new一个Promise对象时,需向其构造函数传入一个function,该function有两个参数分别是resolve和reject,分别代表两个函数,皆由js引擎实现好了,直接调用即可。接下来可以在函数体里做一些异步操作,然后执行成功时调用resolve,并将期望交给下一步的值(如服务器端的response)传给resolve;失败时调用reject,使用方法同理。举个用ajax模拟fetch的例子:
function fetchMessage(url) { return new Promise(function(resolve, reject) {var xhr = new XMLHttpRequest(); xhr.open("GET",url); xhr.send(null); xhr.onreadystatechange = function() { if ((xhr.readyState == 4) && (xhr.status == 200)) { resolve(xhr.responseText); } else { reject(new Error(‘Ajax fail‘)); } } }); } fetchMessage("105.108.99.6").then(function (resText){ console.log(resText);}).catch(function (error){ console.error("Error", error);}};
这段代码里,我们调用fetchMessage函数获得一个Promise对象,即“得到一份承诺”,然后我们调用它的then方法传入一个回调函数,这个回调在resolve被调用后被调用,传入resolve的实参会被传给这个回调,可以理解为“承诺兑现之后,你就干这个”,then方法也会返回一个Promise对象,并且将回调的返回值传给resolve,如此可以形成链式调用,彻底解决;catch方法与then类似,但它接收的回调在reject被调用后调用,用于捕获处理异常情况,理解为“若是承诺未能兑现,那么你就这么办”。注意,这里的catch实际上是被then方法返回的Promise对象所调用,这说明了Promise的另一个性质:传导性。前一个Promise若是执行了resovle,则其then方法返回的Promise也将立即执行resovle;前一个Promise若是执行了reject,则其then方法的回调将不被调用,then方法仍将返回一个Promise,但立即执行其reject并将错误继续传递下去,直到被catch或调用链结束。
好了,讲到这里Promise对象应该会用了吧?让ES6带你远离回调金字塔的噩梦,把异步代码写出同步的快感!