async+await优雅处理异步

同步与异步

1.同步

一次只能执行一个任务,函数调用后需等到函数执行结束,返回执行的结果,才能进行下一个任务。如果这个任务执行的时间较长,就会导致「线程阻塞」。

/* 例2.1 */
var x = true;
while(x);
console.log("don't carry out");    //不会执行

2.js的单线程与异步

我们知道,js的执行环境是「单线程」
说到js的单线程(single threaded)和异步(asynchronous),很多同学不禁会想,这不是自相矛盾么?其实,单线程和异步确实不能同时成为一个语言的特性。js选择了成为单线程的语言,所以它本身不可能是异步的,但js的宿主环境(比如浏览器,Node)是多线程的,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性。

3.浏览器为耗时的任务开辟另外的线程

js是单线程语言,浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如http网络请求定时器事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。

4.任务队列

刚才说到浏览器为网络请求这样的异步任务单独开了一个线程,那么问题来了,这些异步任务完成后,主线程怎么知道呢?答案就是回调函数,整个程序是事件驱动的,每个事件都会绑定相应的回调函数,举个栗子,有段代码设置了一个定时器

setTimeout(function(){
    console.log(time is out);
},50);

执行这段代码的时候,浏览器异步执行计时操作,当50ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。整个程序就是通过这样的一个个事件驱动起来的。
所以说,js是一直是单线程的,浏览器才是实现异步的那个

要实现按顺序打印

function ajax(word) {
    setTimeout(() => {
        console.log(word)
    },1000) 
}
ajax('1')
console.log('2')

上面这段代码,打印出2,1,因为setTimeout是异步,js遇到异步会先挂起,先执行同步任务。

1.用回调的方法解决

function ajax1(word,fn) {
    setTimeout(() => {
        console.log(word)
        fn()
    },1000)
}
ajax1('1',() => {
    ajax1('2',() => {
        ajax1('3',() => {
            ajax1('4',() => {
        
            })
        })
    })
})

如果项目复杂,就会有请求1,请求2,这就是传说中的回调地狱,简称callback hell

2.promise解决方案

function delay(word) {
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            resolve(word)
        },2000)
    })
}

delay('孙悟空')
    .then((word) => {
        console.log(word)
        return delay('猪八戒')
    })
    .then(word => {
        console.log(word)
        return delay('沙悟净')
    })
    .then(word => {
        console.log(word)
    })

3.async+await

function delay(word) {
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            resolve(word)
        },2000)
    })
}
//async+await一定要一起使用,async定义函数,await在async内部使用,等待异步执行完毕
async function start() {
    const word1 = await delay('孙悟空')
    console.log(word1)
    const word2 = await delay('猪八戒')
    console.log(word2)
    const word3 = await delay('沙悟净')
    console.log(word3)
}
start()

下面用一个小球运动的例子来描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
        .ball{
            width: 40px;height: 40px;border-radius: 20px;
        }
        .ball1{
            background: red;
        }
        .ball2{
            background: yellow;
        }
        .ball3{
            background: #ccc;
        }
    </style>
</head>
<body>
    <div class="ball ball1" style="margin-left:0"></div>
    <div class="ball ball2" style="margin-left:0"></div>
    <div class="ball ball3" style="margin-left:0"></div>
    <script type="text/javascript">
        var ball1 = document.querySelector('.ball1');
        var ball2 = document.querySelector('.ball2');
        var ball3 = document.querySelector('.ball3');

//用回调的方法写动画
        function animate(ball,distance,cb){
            setTimeout(function(){
                var marginLeft = parseInt(ball.style.marginLeft,10)
                if(marginLeft === distance){
                    cb && cb()
                }else{
                    if(marginLeft < distance){
                        marginLeft++
                    }else{
                        marginLeft--
                    }
                    ball.style.marginLeft = marginLeft + 'px'
                    animate(ball,distance,cb)
                }
            },13)
        }


//调用
        animate(ball1,100,function(){
            animate(ball2,200,function(){
                animate(ball3,300,function(){
                    animate(ball3,150,function(){
                        animate(ball2,150,function(){
                            animate(ball1,150,function(){
            
                            })
                        })
                    })
                })
            })
        })

//----------------------------------------------------
//promise方法写动画
        function promiseAnimate(ball,distance){
            return new Promise(function(resolve,reject){
                function _animate(){
                    setTimeout(function(){
                        var marginLeft = parseInt(ball.style.marginLeft,10);
                        if(marginLeft === distance){
                            resolve();
                        }else{
                            if(marginLeft < distance){
                                marginLeft++
                            }else{
                                marginLeft--
                            }
                            ball.style.marginLeft=marginLeft + 'px'
                            _animate(ball,distance);
                        }
                    },13);
                }
                _animate();
            })
        }

//调用
        promiseAnimate(ball1,100)
            .then(function(){
                return promiseAnimate(ball2,200);
            })
            .then(function(){
                return promiseAnimate(ball3,300);
            })
            .then(function(){
                return promiseAnimate(ball3,150);
            })
            .then(function(){
                return promiseAnimate(ball2,150);
            })
            .then(function(){
                return promiseAnimate(ball1,150);
            })


//async+await方法

        async function start() {
            await promiseAnimate(ball1,100)
            await promiseAnimate(ball2,200)
            await promiseAnimate(ball3,150)
            await promiseAnimate(ball2,150)
            await promiseAnimate(ball1,150)
        }
        start()
    </script>
</body>
</html>

简单的ajax实现方法

//--------------------回调-------------------------------------------------------------------------
var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';
var result;

var XHR = new XMLHttpRequest();
XHR.open('GET', url, true);
XHR.send();

XHR.onreadystatechange = function() {
    if (XHR.readyState == 4 && XHR.status == 200) {
        result = XHR.response;
        console.log(result);

        // 伪代码
        var url2 = 'http:xxx.yyy.com/zzz?ddd=' + result.someParams;
        var XHR2 = new XMLHttpRequest();
        XHR2.open('GET', url, true);
        XHR2.send();
        XHR2.onreadystatechange = function() {
            //...
        }
    }
}

//---------promise----------------------------------------------------------------------------------------

var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';

// Promise封装一个get请求的方法
function getJSON(url) {
    return new Promise(function(resolve, reject) {
        var XHR = new XMLHttpRequest();
        XHR.open('GET', url, true);
        XHR.send();

        XHR.onreadystatechange = function() {
            if (XHR.readyState == 4) {
                if (XHR.status == 200) {
                    try {
                        var response = JSON.parse(XHR.responseText);
                        resolve(response);
                    } catch (e) {
                        reject(e);
                    }
                } else {
                    reject(new Error(XHR.statusText));
                }
            }
        }
    })
}

getJSON(url).then(resp => console.log(resp));

//-----------------------async------------------------------------------------------------------------

function getUrl(url) {
    return new Promise((resolve,reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open('GET',url,true)
        xhr.send()
        xhr.onreadystatechange = function() {
            if(xhr.readyState == 4){
                if(xhr.status == 200) {
                    resolve(JSON.parse(xhr.response))
                }else{
                    reject(new Error(XHR.statusText));
                }
            }
        }
    }) 
}

async function start() {
    const word1 = await getUrl('https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10')
    console.log(word1)
}
start()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容