为什么需要异步编程我们写前端代码的时候,经常会对dom做事件处理操作,比如点击,激活焦点,失去焦点等.;再比如我们使用Ajax请求数据,使用回调函数获取返回值。这些都是异步编程。也许你已经知道单线程JavaScript引擎的概念,那么这种单线程模式和异步编程有什么关系呢?在JavaScript引擎中,只有一个主线程。当一个JavaScript代码块执行时,其他代码块不允许执行,事件机制和回调机制的代码块会被加入到任务队列(或称为栈)中。当满足某个触发条件时,回调或事件被调用时,该事件或回调函数就会被执行。上一段的意思可以这样理解。假设你是一个修炼者,你去了一个秘境。这个秘境就是主线。对象空间,这个空间类似于一个栈,你在存储空间里放了很多你可能用到的法宝或者丹药,这些东西就是回调函数和事件函数,当你遇到危险或者满足某种条件的时候,你可以立即从存储中获取您需要的东西。好了,话不多说,下面来看正题。事件模型:当浏览器第一次渲染DOM时,我们会绑定一些DOM事件函数,只有当这些DOM事件函数被触发时,才会执行。constbtn=document.querySelector('.button')btn.onclick=function(event){console.log(event)}回调方式:这种回调方式在nodejs中可能很常见,但是对于前端来说,ajax回调就可以了是最熟悉的。ajax回调有多种状态,响应成功和失败时有不同的回调函数。$.post('/router',function(data){console.log(data)})callback也可能会带来一个问题,那就是地狱回调,还好我从进入前端世界开始就用了react,我跳过了很多陷阱,尤其是地狱回调,我一直没有机会在工作中遇到它。真遗憾。Promise事件函数没有问题。我们用得很好。问题出在回调函数上,尤其是地狱回调。Promise的出现就是为了避免地狱回调带来的麻烦。推荐大家阅读JavaScriptMDNPromise教程,再结合本文阅读,即可学会使用Promise。什么是PromisePromise在中文里是承诺的意思,也就是说,JavaScript对你做出承诺,并会在未来的某个时刻兑现承诺。Promise生命周期React有生命周期,vue也有生命周期,连Promise都有生命周期,为什么现在这么流行生命周期。Promise的生命周期:pending,fulfilled,rejected回调异常时,使用catch()。这次我们以axios插件的代码为例。axios是前端比较流行的http请求插件之一。1.创建一个axios实例实例。importaxiosfrom'axios'exportconstinstance=axios.create()2.使用axiosinstance+Promise获取返回值。constpromise=instance.get('url')promise.then(result=>console.log(result)).catch(err=>console.log(err))使用Promise构造函数创建新的PromisePromise构造函数只有一个参数,参数是一个函数,叫做执行器,执行器有2个参数,resolve()和reject(),一个回调表示成功,一个回调表示失败。newPromise(function(resolve,reject){setTimeout(()=>resolve(5),0)}).then(v=>console.log(v))//5记住,Promise实例只能通过resolve或者reject函数返回,使用then()或者catch()获取,在newPromise中不能直接return,所以获取不到Promise的返回值。1、我们也可以直接使用Promise来resolve(value)。Promise.resolve(5).then(v=>console.log(v))//52.你也可以使用reject(value)Promise.reject(5).catch(v=>console.log(v))//53.Actuator错误被catch捕获。newPromise(function(resolve,reject){if(true){thrownewError('error!!')}}).catch(v=>console.log(v.message))//错误!!globalPromise拒绝处理不重要的内容,不用细看。这涉及到nodejs环境和浏览器环境的整体情况。重点是如果执行Promise.reject(),浏览器或node环境不会强制报错。只有当你调用catch时,你才能知道Promise已被拒绝。.这种行为就像,你写一个函数,函数内部有true和false两种状态,我们想在为false的时候抛出错误,但是在Promise中,你不能直接抛出错误,不管是不是Promise是否成功。被拒绝的状态,唯一能得到Promise生命周期的方法就是通过then()和catch()。Nodejs环境:node环境中有一个叫做process的对象。即使你没有写过后端节点,如果你写过前端节点服务器,你应该知道你可以使用process.ENV_NODE来获取环境变量。为了监听Promise的拒绝(rejection),NodeJS提供了一个process.on(),类似于jQuery的on方法和一个事件绑定函数。process.on()有2个事件unhandledRjection:在事件循环中,当Promise执行reject()时,没有提供catch()。通常,您可以使用catch来捕获拒绝。Promise.reject("这是我的错!").catch(v=>console.log(v))然而,有时你并不总是记得使用catch。你需要使用process.on()letrejectedrejected=Promise.reject("Itwasmywrong!")process.on("unhandledRjection",function(reason,promise){console.log(reason.message)//它是我错了!console.log(rejected===promise)//true})rejectionHandled:当Promise拒绝并且没有提供catch()时调用,在事件循环之后。letrejectedrejected=Promise.reject(newError("这是我的错!"))process.on("rejectionHandled",function(promise){console.log(rejected===promise)//true})异同点:events在循环中和事件循环之后,你可能很难理解两者的区别,但这并不重要。重要的是,如果你使用catch()方法来捕获reject操作,那么这两个事件是不会生效的。浏览器环境:和node环境一样,都提供unhandledRjection和rejectionHandled事件。不同的是,浏览器环境通过window对象来定义事件函数。letrejectedrejected=Promise.reject(newError("Itwasmywrong!"))window.rejectionHandled=function(event){console.log(event)//true}rejectionHandled()在浏览器控制台执行一次代码,你会发现一个错误:Uncaught(inpromise)错误:是我错了!耶,你成功了!报错的内容正是你写的reject()方法中的报错信息。在这个Promise链调用的例子中,使用了3个then,第一个then返回s*s,第二个then捕获前一个then的返回值,最后一个then直接输出end。这叫做链式调用,很好理解。我只用了then(),在实际开发中,还应该加上catch()。newPromise(function(resolve,reject){try{resolve(5)}catch(error){reject('这是我的错误!!!')}}).then(s=>s*s).then(s2=>console.log(s2)).then(()=>console.log('end'))//25“end”Promise的其他方法在Promise的构造函数中,除了reject()和resolve(),有两个方法,Promise.all(),Promise.race()。Promise.all():在前面的例子中,只有一个Promise。现在我们使用all()方法来包装多个Promise实例。语法很简单:只有一个参数,一个可迭代对象,可以是数组,也可以是Symbol类型等Promise.all(iterable).then().catch()例子:传入3个Promise实例Promise.all([newPromise(function(resolve,reject){resolve(1)}),newPromise(function(resolve,reject){resolve(2)}),newPromise(function(resolve,reject){resolve(3)})]).then(arr=>{console.log(arr)//[1,2,3]})Promise.race():语法和all()一样,但是返回价值不同。根据传入的多个Promise实例,race只会在一个实例resolve或者reject时返回结果,其他实例不会再Execute。还是用上面的例子,但是我每次resolve都加了一个timer,最后的结果是3,因为第三个Promise执行的最快。Promise.race([newPromise(function(resolve,reject){setTimeout(()=>resolve(1),1000)}),newPromise(function(resolve,reject){setTimeout(()=>resolve(2)),100)}),newPromise(function(resolve,reject){setTimeout(()=>resolve(3),10)})]).then(value=>{console.log(value)//3})PromiseDerivation派生就是定义一个新的Promise对象,继承Promise的方法和属性。classMyPromiseextendsPromise{//重新打包then()success(resolve,reject){returnthis.then(resolve,reject)}//重新打包catch()failer(reject){returnthis.catch(reject)}}然后让我们使用这个派生类。newMyPromise(function(resolve,reject){resolve(10)}).success(v=>console.log(v))//10如果你只是推导和then一样的方法然后catch,我不认为你会做这么无聊的事。Promise与异步连接Promise本身不是异步的,只是它的then()或catch()方法是异步的,也可以说Promise的返回值是异步的。通常Promise用在node,或者前端ajax请求,前端DOM渲染顺序等,比Promise更强大的异步解决方案。本章你只需要了解async未来的解决方案即可。如果没有,建议去网上找资料。反正我在实际项目中已经完全实现了async。asyncfunctiona(){awaitfunction(){}}}总结什么是Promise,如何使用,如何获取返回值?这是本章的中心内容。多看几遍,你会发现Promise的使用非常简单。=>返回文章目录
