问题引出:php
思考(案例来自stackoverflow):html
function foo(){ var result; $ajax({ url:'...', success:function(response){ result=response; //return response;//tried this one as well } }); return result; } var result=foo();
初学异步的时候,这里是很容易错的地方,你想要获取从服务器端返回的数据,结果却一直undefined。ajax
!!!气死我了编程
在弄清楚此概念以前,先了解 JS 的异步机制:segmentfault
参考连接:http://www.javashuo.com/article/p-xackczmh-cr.html数组
http://www.javashuo.com/article/p-xqrpyxku-dd.htmlpromise
http://www.javashuo.com/article/p-ffewwzss-bq.html浏览器
http://www.ruanyifeng.com/blog/2015/05/async.html服务器
首先先了解什么是同步和异步:多线程
同步:
在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;就像上面的图中同样,线性执行
异步:
若是函数是异步的,这个函数在被调用的时候,会立刻返回一个结果,可是这个结果可能不是预期的哦。(这是由于这个函数尚未获得最终的结果,可是又不能让人家一直等着,因此就先返回一个结果,致使不阻塞住)。等到这个函数终于知道结果了,若是有调用他的,他才告诉人家正确的结果。
下面以AJAX请求为例,来看一下同步和异步的区别:
主线程:“你好,AJAX线程。请你帮我发个HTTP请求吧,我把请求地址和参数都给你了。”
AJAX线程:“......”
主线程::“喂,AJAX线程,你怎么不说话?”
AJAX线程:“......”
主线程::“喂!喂喂喂!”
AJAX线程:“......”
(一炷香的时间后)
主线程::“喂!求你说句话吧!”
AJAX线程:“主线程,很差意思,我在工做的时候不能说话。你的请求已经发完了,拿到响应数据了,给你。”
主线程:“你好,AJAX线程。请你帮我发个HTTP请求吧,我把请求地址和参数都给你了。”
AJAX线程:“好的,主线程。我立刻去发,但可能要花点儿时间呢,你能够先去忙别的。”
主线程::“谢谢,你拿到响应后告诉我一声啊。”
(接着,主线程作其余事情去了。一顿饭的时间后,它收到了响应到达的通知。)
单线程语多线程、JS 单线程:
JavaScript其实就是一门语言,说是单线程仍是多线程得结合具体运行环境。JS的运行一般是在浏览器中进行的,具体由JS引擎去解析和运行。下面咱们来具体了解一下浏览器。
浏览器:
目前最为流行的浏览器为:Chrome,IE,Safari,FireFox,Opera。浏览器的内核是多线程的。
一个浏览器一般由如下几个常驻的线程:
须要注意的是,渲染线程和JS引擎线程是不能同时进行的。渲染线程在执行任务的时候,JS引擎线程会被挂起。由于JS能够操做DOM,若在渲染中JS处理了DOM,浏览器可能就不知所措了。
JS引擎:
一般讲到浏览器的时候,咱们会说到两个引擎:渲染引擎和JS引擎。渲染引擎就是如何渲染页面,Chrome/Safari/Opera用的是Webkit引擎,IE用的是Trident引擎,FireFox用的是Gecko引擎。不一样的引擎对同一个样式的实现不一致,就致使了常常被人诟病的浏览器样式兼容性问题。这里咱们不作具体讨论。
JS引擎能够说是JS虚拟机,负责JS代码的解析和执行。一般包括如下几个步骤:
不一样浏览器的JS引擎也各不相同,Chrome用的是V8,FireFox用的是SpiderMonkey,Safari用的是JavaScriptCore,IE用的是Chakra。
之因此说JavaScript是单线程,就是由于浏览器在运行时只开启了一个JS引擎线程来解析和执行JS。那为何只有一个引擎呢?若是同时有两个线程去操做DOM,浏览器是否是又要不知所措了。
因此,虽然JavaScript是单线程的,但是浏览器内部不是单线程的。一些I/O操做、定时器的计时和事件监听(click, keydown...)等都是由浏览器提供的其余线程来完成的。
咱们常说“JavaScript是单线程的”。所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个。不妨叫它主线程。
可是实际上还存在其余的线程。例如:处理AJAX请求的线程、处理DOM事件的线程、定时器线程、读写文件的线程(例如在Node.js中)等等。这些线程可能存在于JS引擎以内,也可能存在于JS引擎以外,在此咱们不作区分。不妨叫它们工做线程。
正是因为JavaScript是单线程的,而异步容易实现非阻塞,因此在JavaScript中对于耗时的操做或者时间不肯定的操做,使用异步就成了必然的选择。
function foo(callback){//定义函数的时候将另外一个函数(回调函数)做为参数传入定义的函数中。 $ajax({ //... success:callback//异步操做执行完毕后,再执行该回调函数,确保回调在异步操做以后执行。 }); } function myCallback(result){ //... } foo(myCallback);
先看上面这个函数是什么意思: ajax 函数执行,而后是两个 then 方法和一个 catch 方法 =》 ajax 里面是啥呢? 是返回了一个 Promise 的东西 =》 Promise 里面是啥呢? 是一系列函数执行,我也不知道这是执行的啥,反正就是在执行。
那就看看这个 Promise 是个什么东西?烦死了,弄的稀奇古怪的。
Promise表明了一个异步操做,能够将异步对象和回调函数脱离开来,经过.then方法在这个异步操做上绑定回调函数,Promise可让咱们经过链式调用的方法去解决回调嵌套的问题。先捋一下, 应该就是 Promise 里面的是一个异步操做,then 函数是在他执行完以后才回去挨个执行的,应该是这个意思吧,和上面那种比就是换了个写法嘛。
promise对象存在三种状态:
1)Fulfilled:成功状态
2)Rejected:失败状态
3)Pending:既不是成功也不是失败状态,能够理解为进行中状态
promise对象的两个重要方法:resolve/reject
1)resolve方法可使Promise对象的状态改变为成功,同时传递一个参数用于后续成功后的操做。
2)reject方法能够将Promise对象的状态改变为失败,同时将错误信息传递到后续错误处理的操做。
.then可使用链式调用,缘由在于:每一次执行该方法时总会返回一个Promise对象(Promise 对象的意义就是告诉成功仍是失败吧)。
另外,在then的函数当中的返回值,能够做为后续操做的参数(例如:.then(return a).then(console.log(a+b))),这不就是线性执行了嘛!!!
理解Promise用法的关键点:
1.then方法是Promise实例的方法,即Promise.prototype上的,它的做用是为Promise实例添加状态改变时的回调函数,这个方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
2.链式中的第二个then开始,它们的resolve中的参数,是前一个then中resolve的return语句的返回值。
3.关于执行顺序:Promise在实例化的时候就会执行,也就是若是Promise的实例化语句中函数console.log输出语句,它会比then中的先执行。Promise.all中传入的Promise对象的数组(假设为p一、p2),即便p2的运行速度比p1快,Promise.all方法仍然会按照数组中的顺序将结果返回。
理解了上面这些方便写原生的Promise,利用观察者模式。后面补充。
Promise的缺点:
1.当处于未完成状态时,没法肯定目前处于哪一阶段。
2.若是不设置回调函数,Promise内部的错误不会反映到外部。
3.没法取消Promise,一旦新建它就会当即执行,没法中途取消。
3.async/await:
不少人说async/await是异步编程的终极解决方案、
JavaScript 的 async/await 实现,离不开 Promise。
看上面这段代码: 有一个 delay 函数,里面有Promise关键字(代表是异步操做,你们让让,我先执行)。 在看 getAllBooks 函数, 它里面有 await 这个关键字(意如其名,你们等等,等下面这个执行完了才能执行别的)
上面的 delay() 没有申明为 async。实际上,delay() 自己就是返回的 Promise 对象,加不加 async 结果都同样。
只要在函数名以前加上async关键字,就代表这个函数内部有异步操做。这个异步操做返回一个Promise对象,前面用await关键字注明。函数执行的时候,一旦遇到await,就会先执行await后面的表达式中的内容(异步),再也不执行函数体后面的语句。等到异步操做执行完毕后,再自动返回到函数体内,继续执行函数体后面的语句。
async:定义异步函数1)自动把函数转换为Promise
2)当调用异步函数时,函数返回值会被resolve处理
3)异步函数内部可使用await
await:暂停异步函数的执行
1)当使用在Promise前面时,await等待Promise完成,并返回Promise的结果
2)await只能和Promise一块儿使用,不能和callback一块儿使用
3)await只能用在async函数中
async/await并不会取代promise,由于async/await底层依然使用promise。
每次遇到 await 关键字时,Promise 都会停下在,await 把异步变成了同步。