zoukankan      html  css  js  c++  java
  • async await 了解

    问题引出:

    思考(案例来自stackoverflow):

    function foo(){
        var result;
        $ajax({
            url:'...',
            success:function(response){
                result=response;
                //return response;//tried this one as well
            }
        });
        return result;
    }
    var result=foo();

    初学异步的时候,这里是很容易错的地方,你想要获取从服务器端返回的数据,结果却一直undefined。

    !!!气死我了


    在弄清楚此概念之前,先了解 JS 的异步机制:

    参考链接:https://juejin.im/post/5a6ad46ef265da3e513352c8

    https://segmentfault.com/a/1190000013141641

    https://segmentfault.com/a/1190000004322358

    http://www.ruanyifeng.com/blog/2015/05/async.html

    首先先了解什么是同步和异步

    同步:

     在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;就像上面的图中一样,线性执行

    异步:

    如果函数是异步的,这个函数在被调用的时候,会马上返回一个结果,但是这个结果可能不是预期的哦。(这是因为这个函数还没有得到最终的结果,但是又不能让人家一直等着,所以就先返回一个结果,导致不阻塞住)。等到这个函数终于知道结果了,如果有调用他的,他才告诉人家正确的结果。

    下面以AJAX请求为例,来看一下同步和异步的区别:

    • 同步ajax

    主线程:“你好,AJAX线程。请你帮我发个HTTP请求吧,我把请求地址和参数都给你了。”

    AJAX线程:“......”

    主线程::“喂,AJAX线程,你怎么不说话?”

    AJAX线程:“......”

    主线程::“喂!喂喂喂!”

    AJAX线程:“......”

    (一炷香的时间后)

    主线程::“喂!求你说句话吧!”

    AJAX线程:“主线程,不好意思,我在工作的时候不能说话。你的请求已经发完了,拿到响应数据了,给你。”

    • 异步ajax

    主线程:“你好,AJAX线程。请你帮我发个HTTP请求吧,我把请求地址和参数都给你了。”

    AJAX线程:“好的,主线程。我马上去发,但可能要花点儿时间呢,你可以先去忙别的。”

    主线程::“谢谢,你拿到响应后告诉我一声啊。”

    (接着,主线程做其他事情去了。一顿饭的时间后,它收到了响应到达的通知。)

     单线程语多线程、JS 单线程:

    在上面介绍异步的过程中就可能会纳闷:既然JavaScript是单线程,怎么还存在异步,那些耗时操作到底交给谁去执行了?

    JavaScript其实就是一门语言,说是单线程还是多线程得结合具体运行环境。JS的运行通常是在浏览器中进行的,具体由JS引擎去解析和运行。下面我们来具体了解一下浏览器。

    浏览器:

    目前最为流行的浏览器为:Chrome,IE,Safari,FireFox,Opera。浏览器的内核是多线程的。

    一个浏览器通常由以下几个常驻的线程:

    • 渲染引擎线程:顾名思义,该线程负责页面的渲染
    • JS引擎线程:负责JS的解析和执行
    • 定时触发器线程:处理定时事件,比如setTimeout, setInterval
    • 事件触发线程:处理DOM事件
    • 异步http请求线程:处理http请求

    需要注意的是,渲染线程和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中对于耗时的操作或者时间不确定的操作,使用异步就成了必然的选择。

     
     
    解决办法:
    1. 通过回调函数解决
    function foo(callback){//定义函数的时候将另一个函数(回调函数)作为参数传入定义的函数中。
        $ajax({
            //...
            success:callback//异步操作执行完毕后,再执行该回调函数,确保回调在异步操作之后执行。
        });
    }
    function myCallback(result){
        //...
    }
    foo(myCallback);
     
    上面一段代码解释: 我们定义了两个函数 myCallback 和 foo。
      foo 函数里面呢,是有异步操作的(ajax),按照正常情况,他会返回一个不是真正结果的值,那我们怎么样才能得到真正的结果呢?就是当这个函数执行完的时候,调用 myCallback(这个函数是通过参数传进来的),得到这个真正的结果,就解决了这个问题。
    优点:比较容易理解;
    缺点:1.高耦合,维护困难,回调地狱; 2.每个任务只能指定一个回调函数; 3.如果几个异步操作之间并没有顺序之分,同样也要等待上一个操作执行结束再进行下一个操作。
     
    2. Promise
     

    先看上面这个函数是什么意思: 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))),这不就是线性执行了嘛!!!

     那么问题来了,如果上面代码异步操作抛出错误,会怎么样?会调用catch方法指定的回调函数,处理这个错误,而且then方法指定的回调函数,如果运行中抛出错误,也会被catch捕获。Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止,也就是说,错误总是会被下一个catch语句捕获。
     

    理解Promise用法的关键点:
    1.then方法是Promise实例的方法,即Promise.prototype上的,它的作用是为Promise实例添加状态改变时的回调函数,这个方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
    2.链式中的第二个then开始,它们的resolve中的参数,是前一个then中resolve的return语句的返回值。
    3.关于执行顺序:Promise在实例化的时候就会执行,也就是如果Promise的实例化语句中函数console.log输出语句,它会比then中的先执行。Promise.all中传入的Promise对象的数组(假设为p1、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 把异步变成了同步。

    猪猪侠要努力呀!
  • 相关阅读:
    浅析C#中的Thread ThreadPool Task和async/await
    C#中的集合类型
    WPF: StaticResource vs DynamicResource
    .NET程序员需要了解的概念、名词、术语--持续更新
    VisualStudio常见问题
    一些有用的.NET开源库--持续更新
    在MSBuild中使用Task实现自动引用指定版本的NuGet包
    C#与JMS的连接问题
    EventLog学习记录
    windows app设计原则总结-持续更新...
  • 原文地址:https://www.cnblogs.com/mlllily/p/10983762.html
Copyright © 2011-2022 走看看