async/await把咱们从回调地狱中解放了出来,可是随之而来的是你们对它的滥用,致使了async/await地狱的诞生。
在这篇文章里我将会解释什么是async/await地狱以及分享一些方法去避开它。前端
当咱们在编写JavaScript异步代码的时候,经常会在一个接着一个函数调用前添加await关键字,影响了函数的调用。由于在通常状况下,下一个函数的调用并不依赖于前一个函数的调用,可是正是由于添加了await关键字,咱们仍须要等待前一个函数调用完毕才能调用下一个函数。数组
假设咱们要写一段代码用于购买披萨和饮料,这段代码以下所示:promise
从表面上看这段代码没有问题而且可以运行,可是这不是一个好的方法由于没有考虑到并发问题。接下来让咱们仔细分析一下这段代码而后明确其中问题所在。并发
咱们把这段代码包裹在了一个异步当即执行函数里,下列事件会依次发生:异步
就像我在前面提到的那样,全部的代码都是一行接着一行执行的,这里不存在并发执行的状况。让咱们仔细想一想,为何咱们在获取饮料列表以前须要等待披萨列表的返回?咱们应该尝试同时获取饮料和披萨的列表。然而,当咱们须要选择披萨的时候,咱们须要先获取披萨的列表。饮料也是如此。
所以咱们能够得出结论:披萨相关的工做和饮料相关的工做可以同时执行,可是披萨相关的每一步工做须要按次序执行。async
下面这段JavaScript代码会获取购物车里面的物品,而后发送确认订单的请求。函数
在这种状况下,for循环在执行下一轮循环以前须要等待当前的sendRequest()函数执行完成。然而,实际上咱们不须要等待,咱们但愿尽量快的发送全部请求而后等待他们都完成执行。
如今,我但愿你们可以清晰的理解什么是async/await地狱以及它们对程序的性能影响的严重性。如今,我要问一个问题。oop
若是你忘记在异步函数调用的前面添加await关键字,这时函数开始执行了,这意味着await并非函数执行的必要条件。这个异步函数会返回一个promise,这个promise咱们能够在以后使用。性能
另外一个后果就是编译程序不知道你须要等待这个函数执行完成,所以编译程序会在这个异步任务尚未完成的时候退出这个程序,因此咱们须要await关键字。
promise有一个有趣的性质是:你能够在前面的代码获得这个promise, 而后在后面的代码中等待这个promise的完成。这是从async/await地狱中解脱的关键。spa
正如你所见到的那样,doSomeAsyncTask()
返回了一个promise。这个时候,doSomeAsyncTask()已经开始执行了。为了获得这个promise的结果值,咱们能够在这个promise前面添加await,JavaScript将会马上停在这里再也不执行下一行代码,直到得到了这个promise的返回值,再执行下一行代码。
依照如下步骤来逃离async/await地狱。
在咱们第一个例子里面,咱们在选择披萨和饮料。于是咱们得出结论:在选择披萨以前,咱们须要得到披萨的列表;在把披萨加入到购物车以前,咱们须要选择披萨。咱们能够认定这三个步骤是互相依赖的,咱们没法在前一个步骤完成以前执行下一个任务。可是,若是咱们退一步来看,就会发现选择披萨并不依赖于选择饮料,咱们能够同时对他们进行选择。在这一件事情上,机器能比人类作得更好。
因此咱们已经发现了一些语句依赖于其余的语句执行而另一些语句并不依赖于其余。
正如咱们所看到的,选择披萨须要如下几个互相依赖的语句:得到披萨列表, 选择其中一个披萨以及添加到购物车中。咱们应该把这些语句整合在一个异步函数里面。这样咱们将会获得两个异步函数,selectPizza()和
selectDrink()。
咱们将利用event loop的优点来并发的执行这些非阻塞异步函数。经常使用的一个方法是先返回promise而后使用Promise.all方法。
根据前面提到的三个步骤,咱们把他们运用的咱们的例子中。
如今,咱们已经把这些代码整合到两个函数中。在每个函数里面,每一条代码的执行依赖于前一条代码的执行。而后咱们并发的执行selectPizza()
和selectDrink()
。
对于例2,咱们须要解决未知数量的promise,解决这种状况很是简单:咱们只须要建立一个数组而后把promise存入其中,而后使用Promise.all()
方法,就可以并发的等待全部的promise返回结果。
但愿本文能帮助您看透async/await,并帮助您改进应用程序的性能。