异步操作

同步任务/异步任务,微任务/宏任务
ajax
Promise
generator
await/async

  1. js是单线程的,意思就是一个时间只能处理一个任务。
    举个例子,超市排队结账时,一个收银员一个时间只能对一个客户结账,结完一个才能结下一个。如果有些人买的东西太多了,后面的人就会等很久。就有了同步任务和异步任务
  2. 同步任务和异步任务


    image.png
  • 异步任务会进入Event Table,当时间到了,推入任务队列中。任务队列中先入先出。
  • 主线程执行完毕空闲,才会去读取任务队列,如果任务队列中有任务就推入主程序中执行
  • setTimeout倒计时1000时间到了,并不会一定立马执行回调函数,如果当前主线程中有比较复杂繁琐的任务在执行耗时久,那就意味着时间队列中的任务会延迟执行。总之要看当前主线程中是否空闲,才会去读取任务队列。
  • 异步任务:setTimeout,setInterval,Promise,await/sync,ajax请求,onload加载等
    宏任务:setTimeout,setInterval,ajax请求,onload
    微任务:Promise,await/sync
setTimeout(function(){ //异步任务 
    console.log(2)
},0)
setTimeout(function(){ //异步任务 
    console.log(4)
},1000)
console.log(3) //同步任务
//打印结果 1,3,2,4
  1. ajax原理
    ajax:一种前端与后端的交互方式。异步更新,不重新加载整个网页的情况下,对网页某个部分数据进行局部刷新。
function doAjax(method, url, callback, data, flag) {
    var xhr;
    flag = flag || true;// 默认为true,如果是同步请求,页面会在请求数据时假死
    method = method.toUpperCase();
    // 1.创建XMLHttpRequest对象
    var xhr;
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
    } else { //兼容早期浏览器
        xhr = ActiveXObject('Microsoft.XMLHttp');
    }
    // 2.监听状态变化
    xhr.onreadystatechange = function () {
        if (xhr.readystate === 4 && xhr.status === 200) {
            // 5.读到响应数据
            var res = JSON.parse(xhr.responseText);
            callback && callback(res,xhr.status);
        } else if (xhr.readystate === 4 && xhr.status === 404) {
            callback && callback(res,xhr.status);  //404失败时,把状态码也返回
        }
    }
    // 3.建立连接,4发送请求
    if (method == 'GET') {
        var date = new Date(),
            timer = date.getTime();
        xhr.open('GET', url + '?' + data + '&timer' + timer, flag);
        xhr.send()
    } else if (method == 'POST') {
        xhr.open('POST', url, flag);
        xhr.setRequestHeader('Content-Type', 'application/json');//设置请求头
        xhr.send(data);
    }
}
  1. Promise
  • 实例方法:then、catch、finally
  • 通过new Promise(...)得到的是一个有状态变化的promise对象
  • Promise.resolve(); //一个成功的Promise对象,状态不会改变
  • Promise.reject(); //一个失败的Promise对象,状态不会改变
  • Promise.all(); //监听多个promise都成功
  • Promise.race(); //监听最快的结果
    4.1异步任务,微任务。
console.log(1)
var p = new Promise((resolve,reject)=>{ 
    //注意,Promise的回调函数本身是同步执行的,setTimeout的回调函数才是异步。
    console.log(2);
    setTimeout(function(){
        console.log(3);
        resolve('成功');
    },0)
})
console.log(4);
p.then(function(res){
    console.log(5);
});
// 打印结果:1,2,4,3,5
image.png

状态一旦成功或失败就不能被改变

var p = new Promise((resolve,reject)=>{ 
    resolve(1); //状态改变为fulfilled
    reject(2); //不会被执行
})
p.then(function(res){
    console.log(res) //打印
},function(err){
    console.log(err)
}).finally(()=>{
    //不管成功或失败都会进入
})
// 打印结果:输出1,2没有被输出

4.2 promise的链式写法。
场景:第一个promise成功后再执行第二个promise。
注意:第一个then没有返回promise,默认就会返回一个空的promise,第二个then就是空的promise的结果。

var p = new Promise((resolve, reject) => {
    console.log(1)
    setTimeout(function () {
        resolve('成功');
    }, 0)
})
console.log(2)
p.then(function (res) {
    console.log(3)
    return new Promise((resolve, reject) => { //返回一个promise
        setTimeout(function () {
            resolve(6);
        }, 500)
    })
}).then(function (res) { //第一个then没有返回promise,默认就会返回一个空的promise。
    console.log(res)
})
console.log(5) 
//1,2,5,3,6

把上面ajax请求改为promise,多个时可使用链式写法

function ajaxPromise(url) {
    return new Promise((resolve, reject) => {
        doAjax(url, function (data,status) {
            if (status === 200) { //请求响应成功时
                resolve()
            }else{ //失败时
                reject()
            }
        })
    })
}
ajaxPromise('/a.json')
.then(res => {
    console.log('去请求b')
    return ajaxPromise('/b.json')
},err=>{
     //注意:如果a请求失败,不会去请求b了,但是还是会进入到下面then中,因为默认会返回一个空promise。
}).then(res => {
    console.log('去请求c')
    return ajaxPromise('/c.json')
}).then(res => {
    console.log('c 成功')
})
// 所以链式操作时,要这样写
ajaxPromise('/a.json')
.then(res => {
    console.log('去请求b')
    return ajaxPromise('/b.json')
}).then(res => {
    console.log('去请求c')
    return ajaxPromise('/c.json')
}).then(res => {
    console.log('c 成功')
}).catch(err=>{ //不管上面哪个promise内的请求失败,都会进入到这里,并且不会执行后面的链式语句
    console.log(err)
})

4.3 Promise.reject()/Promise.resolve(),必须返回一个成功/失败的promise对象,又不需要状态变化的那种promise对象。

function foo(flag){
    if(flag){
        return new Promise((resolve,reject)=>{
            console.log(5)
            resolve('success') //异步操作
        })
    }else{
        //当传入false时,如果返回的不是promise对象,foo调用时就不存在then方法了,会报错,所以这里需要返回一个promise对象
        // return 'failed'
        return Promise.reject('failed')
    }
}
foo(false).then(res=>{  
    console.log(res)
}).catch(err=>{
    console.log(err)
})

4.4 Promise.all(),监听多个promise状态都成功,一个失败就会进失败,所有的promise成功才会进then

var p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log(1)
        resolve(1);
    },500)
})
var p2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log(2)
        resolve(2);
    },1000)
})
//监听多个promise状态
Promise.all([p1,p2]).then(res=>{
    //所有的promise都成功才会进来
    console.log('成功',res) 
}).catch(err=>{
    // 只要有一个promise失败,就会进来
    console.log('失败',err)
})
// 打印结果:1 2 成功 [1, 2]

4.5 Promise.race(),race有竞赛的意思。监听多个promise状态,所有结果(成功或失败)中,如果成功最快就会进then,如果失败最快就会进catch,不管是哪个promise的。

var p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log(1)
        reject(1);
    },500)
})
var p2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log(2)
        resolve(2);
    },1000)
})
Promise.race([p1,p2]).then(res=>{
    //如果最快的那个promise结果是成功,就会进这里
    console.log('成功',res) 
}).catch(err=>{
    //如果最快的那个promise结果是失败,就会进这里
    console.log('失败',err)
})
//打印结果:1 失败 1 2

应用场景:图片加载时间超过2s就算超时。

function getImage(){ //图片加载
    return new Promise((resolve,reject)=>{
        var image =  new Image();
        image.onload = function(){
            resolve()
        }
        image.src = '/a.png'
    })
}
function timeout(){ //超时
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            reject()
        },2000)
    })
}
Promise.race([getImage(),timeout()]).then(res=>{
    console.log('成功') 
}).catch(err=>{
    console.log('失败')
})
  1. Generator
    5.1 Generator基本用法,关键字 :* yield next
function* foo(){
    for(let i=0;i<3;i++){
        console.log('a_'+i)
        yield i
        console.log('b_'+i)
    }
}
let f = foo(); //调用foo,函数内部语句并不会执行
console.log(f)
// 调用next,会执行到yield句末
console.log(f.next()) //a_0 {value: 0, done: false}
console.log(f.next()) //b_0 a_1 {value: 1, done: false}
console.log(f.next()) //b_1 a_2 {value: 2, done: false}
// for循环内会执行三次,执行完后,再调用next,状态已变为done: true,但会把yield后的内容执行完,
console.log(f.next()) //b_2 {value: undefined, done: true}

5.2 不能作为构造函数使用。yield只能在 Generator函数 中使用,如下会报错

function* gen(arr){
    arr.forEach(item => {
        yield item+1 //这里会报错,这里是在forEach的回调函数中
    });
}

5.3 yield的值,就是调用next时传入的值

function* gen(x) {
    let y = 2*(yield(x+1)); //yield的结果并不是5+1,而是next()传入的值
    let z  = yield(y/3);
    return x+y+z
}
let g = gen(5)
console.log(g.next()) //{value: 6, done: false}
// next传入12,12就是上次next的yield的结果:yield(x+1)=12.于是y = 2*12;
console.log(g.next(12)) //{value: 8, done: false} 
// yield(y/3) = 13; z=13; x+y+z = 5+24+13
console.log(g.next(13)) //{value: 42, done: true}

应用到ajax请求中

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

推荐阅读更多精彩内容