迟到的js async/await

       上篇刚研究了c#的 async/await,现在又研究起js的 async/await的,我是跟async/await杠上了?非也,学习js的 async/await是因为一个困扰了我好多年的回调地狱问题,现在终于有解决方案了,迟到总比不到好,ES7已经添加了该标准,Chrome新版也已经支持了,当然你想要兼顾其他浏览器那恐怕还得再等等。关于回调地狱这里不再做描述,可以看一下阮一峰老师的文章http://javascript.ruanyifeng.com/advanced/single-thread.html,文中虽没提及回调地狱可他描述的场景就是回调地狱,并且阮老师还给出了串行执行的方案,当然除了该方案还可以用Promise对象的链式写法,不过都还是麻烦毕竟要写回调函数。ECMA也不知道怎么搞得,这么久才意识到这个问题吗?还是压根就没想解决这个问题?async/await这2个关键字太容易让人和c#的async/await联想在一起了,不免让人怀疑是不是参考了c#,因为他们的语法也很像await必须用在async修饰的方法内,await后必须是Promise对象,而c#必须是Task对象。有人说js是参考java的,确实,不过那是以前了,现在java语言发展已经落后c#好多了,不过这并不影响java的受欢迎程度。好了,扯了那么多淡该进入主题了。

  我们先用setTimeout模拟一个异步方法,没办法js里没有线程的概念。然后调用该异步方法并根据返回值做一些业务逻辑。

            function asyncFunction(callback) {
                setTimeout(function () {
                    callback("ok")
                }, 1000);
            }

            $("#btn").click(function () {
                asyncFunction(function (ret) {
                    if (ret == "ok") {
                        alert("成功!");
                    }
                    else {
                        alert("失败!");
                    }
                });
            });

  怎么样?这样写回调方法是不是挺麻烦,现在你也许觉得还行,当你如果有多个异步调用,并且每个异步调用都要根据前一个返回值而做不同处理时,或者这个异步调用在一个循环里,而你不希望一个没结束就进行下一个时你就知道这又多痛苦了。

  好了,再看下ES6中提供的Promise对象的解决方法

            function asyncFunction() {
                return new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        resolve("ok");
                    }, 1000);
                })
            }

            $("#btn").click(function () {
                asyncFunction().then(function (ret) {
                    if (ret == "ok") {
                        alert("成功!");
                    }
                    else {
                        alert("失败!");
                    }
                });
            });

  现在调异步方法时不用传回调函数了,异步方法返回一个承诺,承诺异步调用结束并且成功时执行then中的方法。关于承诺(Promise)对象更多的细节请参考ES6教程,这里不做详细说明。现在这样的写法是不是比之前好像要好一点,最重要的是then里可以继续返回Promise然后继续点then链式调用,似乎解决了回调地狱的问题,但是这样的写法还是不够直观啊,then里面还是要传回调函数啊,只是不用在回调里再回调了,而且放到循环里时还是一件麻烦的事情。

  主角登场,舞台交给async/await,回调函数不用变仍旧返回承诺,看下调用的地方

            $("#btn").click(async function () {
                var ret = await asyncFunction();
                if (ret == "ok") {
                    alert("成功!");
                }
                else {
                    alert("失败!");
                }
            });

  是不是很简单?有人会说这样不是将异步改为同步了吗?没错就是改成同步了,但是神奇的是界面并没有因此而卡住,这个神奇的地方我在上一篇c#的async/await里已探了个究竟,可惜js里没办法显示线程id,如果可以的话我觉得因该和c#一样await后的代码不是在主线程里执行的。我们再做个试验,用ajax的同步和async/await的异步等待做个比较,代码如下:

            //ajax同步
            $("#btn").click(async function () {
                $.ajax("/sys/Test", {
                    type: "get",
                    async: false, //同步执行
                    success: function (r) {
                        alert("ok");
                    }
                });
            });
            //async/await 异步等待
            $("#btn").click(async function () {
                var ret = await new Promise(function (resolve, reject) {
                    $.ajax("/sys/Test", {
                        type: "get",
                        async: true, //异步执行
                        success: function (r) {
                            resolve("ok")
                        }
                    });
                });
                alert(ret);
            });

  后端方法就是简单的线程阻塞1秒钟,在这一秒钟里,采用ajax同步的写法,界面卡死,说明主线程阻塞了,而用async/await异步等待的写法,界面并没卡死,主线程没阻塞。所以异步等待并不是简单的同步。

  好了,到这也该结束了,所有的困扰都在async/await这里解决了,就期待浏览器都普及了。

猜你喜欢

转载自www.cnblogs.com/tigeer/p/9585809.html