资讯专栏INFORMATION COLUMN

[译] 深入理解 Promise 五部曲:1. 异步问题

CHENGKANG / 3557人阅读

摘要:当引擎开始执行一个函数比如回调函数时,它就会把这个函数执行完,也就是说只有执行完这段代码才会继续执行后面的代码。当条件允许时,回调函数就会被运行。现在,返回去执行注册的那个回调函数。

原文地址:http://blog.getify.com/promis...

在微博上看到有人分享LabJS作者写的关于Promise的博客,看了下觉得写得很好,分五个部分讲解了Promise的来龙去脉。从这篇文章开始,我会陆续把五篇博客翻译出来跟大家分享,在大牛的带领下真正理解Promise。卖个关子,作者看待Promise的角度跟我一直以来看到的讲解Promise的角度完全不一样,不只是定留在解决回调金字塔上,至少我没想到Promise竟然有这么重要的意义。先上第一篇。

在这篇文章中,我会解释我们为什么需要使用一个更好的方式(比如Promise)来进行异步流程的编写。

异步

你肯定听说过Javascript中的异步编程,但是它到底是什么呢?

比如当你发生一个Ajax请求,你通常会提供一个回调函数,这个回调函数会在请求返回的时候被调用。但是你是否思考过你的回调函数在其他代码也需要运行的时候是如何被调用的呢?如果两个回调函数同时都要运行会怎样呢?JS引擎会如何处理这个问题呢?

为了理解异步到底是什么,你首先需要理解一个问题:JS引擎是单线程的。这意味着在任何环境中,只有一段JS代码会被执行。但是什么叫一段JS代码呢?总的来说,每个函数是一个不可分割的片段或者代码块。当JS引擎开始执行一个函数(比如回调函数)时,它就会把这个函数执行完,也就是说只有执行完这段代码才会继续执行后面的代码。

换句话说,JS引擎就像一个主题公园中的游乐项目,这个项目每次只能一个人玩儿,人们会排成一个长长的队。大家一个个上去玩儿,下来一个然后再上去一个。如果你要玩儿这个项目你只能在队尾排队等待。幸运的是,每个人都很快就下来了,所以这个队伍移动得很快。

上面说的队伍在技术上被叫做事件轮询。它尽可能快的进行轮询,如果事件队列中有代码需要执行,它会让JS引擎执行这段代码,然后移到下一个需要执行的代码,或者等待新的代码进来。

并发

如果程序在一个时间只有一个任务在执行,这样明显是低效而且有限制性的。如果你点击一个按钮提交一个表单,然后你的鼠标就会被冻结并且你不能滚动页面,这个情况会持续几秒直到请求返回,这样肯定会带来很差的用户体验。

这就是为什么真实的程序会有很多任务在运行而不是就只有一个任务,但是JS引擎是怎么在单线程的环境下实现的呢?

你应该想到每个代码块运行只要很短的时间,通常不到1毫秒。你一眨眼的时间,JS引擎会执行上千百个这样的代码块。但是并不是所有的代码块都是为了执行同一个任务。比如,当你点击提交按钮之后,你也可以点击导航或者滚动页面等等。每个任务都会被分为很多个原子操作,执行这些原子操作会非常快。

比如:

Task A

step1

step2

step3

step4

Task B

step1

step2

JS引擎肯定不能在执行A:1步骤的同时执行B:1。但是Task B不需要等到Task A执行完后再执行,因为引擎可以在每个独立的原子操作之间快速的切换,可能是按下面的顺序执行的:

A:1

B:1

A:2

B:2(Task B完成)

A:3

A:4(Task A完成)

所以,事实上Task A和Task B是可以"同时"运行的,通过穿插地执行它们的每个原子操作,这叫做并发,换句话说,Task A和Task B是并发的。

我们很容易就会把并发和并行弄混。在真正并行的系统中,你会有多个线程,可能一个线程执行Task A同时另一个线程执行Task B。这也意味着,A:1的运行不会阻塞B:1的运行。这就好像有主题公园中有两个分开的游乐项目,会有两队人在排队,它们互相不影响。

JS事件轮询是一个简单的并发模型。它只允许把每个事件添加到事件队列的队尾,而这个队列是先进先出的。当条件允许时,回调函数就会被运行。

同步情况下的异步

在JS中编写异步代码一个巧妙但是烦恼的问题是JS引擎实际执行代码的方式跟我们看上去不大一样。例如:

makeAjaxRequest(url,function(response){
    alert("Response:" + response) ;
}) ;

你会怎么描述这段代码的流程呢?大多数开发者大概会这么说:

发送Ajax请求

等到请求完成的时候,弹出提示框

但是这跟JS引擎实际的执行情况相比还不够准确。这个问题主要是因为我们大脑习惯同步的方式。在上面这个描述中,我们使用“等到。。。的时候”来解释,这就也是说我们会阻塞等待Ajax请求,然后继续执行后面的程序。

JS在步骤1和步骤2之间不会阻塞。一个更准确的描述上面这段代码的方式是:

发送Ajax请求

注册回调函数

继续向下执行

在未来某个时间点,惊呼“Oh,我刚才得到一个返回!”。现在,返回去执行注册的那个回调函数。

这两个解释的区别似乎没什么大不了的,但是我们跳过第三步的思考方式是一个大问题。

源代码是给开发者的而不是计算机的。计算机只关心1和0.有无限种程序能产生一样的1和0序列。我们编写源代码为了使得我们能够以一种有含义并且准确的方式理解代码是干嘛的。由于我们的大脑很难处理异步,所以我们需要找出一种更加同步的方式来编写异步代码,隐藏具体的异步实现。

例如,如果下面这段代码能像我们需要的那样运行并且不会阻塞,那么它是不是更好理解了呢?

response = makeAjaxRequest(url) ;
alert("Response:" + response) ;

如果我们可以像这样编码,那么我们就可以隐藏或者抽象makeAjaxRequest()的异步本质,不需要担心具体细节。
换句话说,我们能使得异步代码只出现在具体的实现上,把这些烦人的东西埋在属于它的地方。

总结

我们还没有解决问题。但是至少我们知道了问题是什么:用异步的方式来表达异步的代码是艰难的,甚至很难用我们的大脑来理解。

我们需要的只是一种以同步的代码来尽可能隐藏具体的异步实现的方式,这样我们的大脑更好理解。我们的目标是以同步的方式来编码而不需要关系它的实现的同步还是异步。

在第二部分:转换的问题中,我会着手处理“回调地狱”来解释这些问题,我们也将看到Promises是如何搞定它的。

深入理解Promise五部曲--1.异步问题
深入理解Promise五部曲--2.转换问题
深入理解Promise五部曲--3.可靠性问题
深入理解Promise五部曲--4.扩展性问题
深入理解Promise五部曲--5.乐高问题

最后,安利下我的个人博客,欢迎访问:http://bin-playground.top

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78171.html

相关文章

  • [] 深入理解 Promise 五部:2. 控制权转换问题

    摘要:直到最近,我们仍然在用简单的回调函数来处理异步的问题。当我们只有一个异步任务的时候使用回调函数看起来还不会有什么问题。 原文地址:http://blog.getify.com/promis... 厦门旅行归来,继续理解Promise 在上一篇深入理解Promise五部曲:1.异步问题中,我们揭示了JS的异步事件轮询并发模型并且解释了多任务是如何相互穿插使得它们看起来像是同时运行的。...

    alanoddsoff 评论0 收藏0
  • [] 深入理解 Promise 五部:4. 扩展问题

    摘要:有一个和相关的更大的问题。最后,请负有责任感并且使用安全的扩展。深入理解五部曲异步问题深入理解五部曲转换问题深入理解五部曲可靠性问题深入理解五部曲扩展性问题深入理解五部曲乐高问题最后,安利下我的个人博客,欢迎访问 原文地址:http://blog.getify.com/promis... 现在,我希望你已经看过深入理解Promise的前三篇文章了。并且假设你已经完全理解Promises...

    Shimmer 评论0 收藏0
  • [] 深入理解 Promise 五部:3. 可靠性问题

    摘要:简单的说,即将到来的标准指出是一个,所以作为一个,必须可以被子类化。保护还是子类化这是个问题我真的希望我能创建一个忠实的给及以下。 原文地址:http://blog.getify.com/promis... 如果你需要赶上我们关于Promise的进度,可以看看这个系列前两篇文章深入理解Promise五部曲--1.异步问题和深入理解Promise五部曲--2.控制权转移问题。 Promi...

    XboxYan 评论0 收藏0
  • [] 深入理解 Promise 五部:5. LEGO

    摘要:一个就像一个乐高玩具。问题是不是你小时候玩儿的那个有趣,它们不是充满想象力的打气筒,也不是一种乐高玩具。这是对的并不是给开发者使用的,它们是给库作者使用的。不会超过这两种情况。第二个是根据第一个处理函数如何运行来自动变成状态成功或者失败。 原文地址:http://blog.getify.com/promis... 在 Part4:扩展问题 中,我讨论了如何扩展和抽象Promise是多么...

    LiveVideoStack 评论0 收藏0
  • 在 PHP 中使用 Promise + co/yield 协程

    摘要:只要在调用异步函数时设置一个或多个回调函数,函数就会在完成时自动调用回调函数。要解决的问题是,如何将回调方法的参数从回调方法中传递出来,让它可以像同步函数的返回结果一样,在回调函数以外的控制范围内,可以传递和复用。 摘要: 我们知道 JavaScript 自从有了 Generator 之后,就有了各种基于 Generator 封装的协程。其中 hprose 中封装的 Promise 和...

    appetizerio 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<