异步操作

同步任务/异步任务,微任务/宏任务
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();

你可能感兴趣的:(异步操作)