javaScript异步编程

javaScript异步编程

  • 一、Promise
    • 1.1、promise基本使用
    • 1.2、promise链式调用
    • 1.3、promise-catch
    • 1.4、promise的静态方法resolve()和reject()
    • 1.5、promise并行执行
    • 1.6、promise执行时序的问题
  • 二、Generator
  • 三、Gererator联合Promise使用
  • 四、Async Await

一、Promise

1.1、promise基本使用

const promise = new Promise(function(resolve,reject){
     
    // 这里用于“兑现”承诺
    
    resolve(100) // 承诺达成
    // reject(new Error('promise error')) // 承诺失败
    
    // 调用resolve或者reject修改状态后就不能再改变状态了
})
promise.then(function(value){
     // resolve回调
    console.log('resolved',value);
},function(error){
     // reject回调
    console.log("rejected",error);
})

1.2、promise链式调用

function ajax(url){
     
    return new Promise(function(resolve,reject){
     
        var xhr = new XMLHttpRequest()
        xhr.open('GET',url)
        xhr.responseType = 'json'
        xhr.onload = function(){
     
            if(this.status == 200){
     
                resolve(this.response)
            }else{
     
                reject(new Error(this.statusText))
            }
        }
        xhr.send()
    })
}
// 手动在根目录下创建一个api文件夹,里面建一个user.json文件
const promise = ajax('/api/user.json') 
const promise2 = promise.then( // then方法的作用就是为Promise对象去添加状态明确后的回调函数  重点是它的内部也会返回一个 Promise 对象
    function onFulfilld(value){
      // onFulfilld 成功过后的回调
        console.log("onFulfilld",value);
    },
    function onRejected(error){
      // onRejected 失败过后的回调
        console.log("onRejected",error);
    }
)
// 每一个then方法都是在为上一个 Promise 对象添加状态明确后的回调
const promise3 = ajax('/api/user.json')
    .then(function(value){
     
        console.log('111');
        return ajax('/api/user.json') // 我们也可以手动返回一个 Promise 对象
    }).then(function(value){
     
        console.log('222',value);
        return "foo"
    }).then(function(value){
     
        console.log('333',value);
    }).then(function(value){
     
        console.log('444');
    })
    
	// 总结:
	// Promise 对象的 then 方法回返回一个全新的 Promise 对象
    // 后面的 then 方法就是在为上一个 then 返回的 Promise 注册回调
    // 前面 then 方法中回调函数的返回值会作为后面 then 方法回调函数的参数
    // 如果回调中返回的是 Promise ,那后面的 then 方法的回调会等待它的结束

1.3、promise-catch

ajax('/api/user.json')
    .then(function(res){
     
        console.log(res);
        return ajax('/error-url')
    })
    .catch(function(err){
      // catch 是为上一个then返回的 Promise 注册异常回调 
        console.log(err);
    })
    
// Promise 上任何一个异常都会一直向后传递,直至被捕获
// catch 方式更像是给整个 Peomise 链条注册的回调,所以更通用些

1.4、promise的静态方法resolve()和reject()

Promise.resolve()会返回一个状态为 fulfilled 的 Promise对象

// ============================== Promise.resolve() ==============================
// 这个 字符串‘foo’ 就会作为返回的 promise对象所返回的值
Promise.resolve('foo')
    .then(function(val){
     
        console.log(val);
    })
// 相当于
new Promise(function(resolve,reject){
     
    resolve('foo')
})

// 如果 Promise.resolve()接收到的是另一个 Promise对象,则会原样返回这个Promise对象
const promise = ajax('/api/user.json')
const promise2 = Promise.resolve(promise)
console.log(promise === promise2);// true

// ============================== Promise.reject() ==============================
Promise.reject(new Error('rejected'))
    .catch(function(err){
     
        console.log(err);
    })
Promise.reject('anything')
    .catch(function(err){
     
        console.log(err);
    })

1.5、promise并行执行

promise.all()可以将多个 Promise 组合成一个 Promise,统一去管理
接收的是一个数组,数组中的每一个元素都是一个 Promise对象
会返回一个全新的 Promise对象
当内部所有的 Promise全部完成之后,这个全新的 Promise才会完成

const promise = Promise.all([
    ajax('/api/user.json'),
    ajax('/api/list.json')
])
// 新 Promise中的Promise全部成功了,才会以成功结束,有一个失败了,就会以失败结束
promise.then(function(val){
     
    // 返回的是一个数组,数组中的每一项是对应的 Promise的结果
    console.log(val);
}).catch(function(err){
     
    console.log(err);
})

promise.race() 只会等待第一个结束的任务,只要有一个任务结束了,它也就结束了
返回结果以第一个结束的任务结果为准

const request = ajax('/api/user.json')
    const timeout = new Promise(function(resolve,reject){
     
        setTimeout(function(){
     
            reject('失败了')
        },500)
    })
    Promise.race([
        request,
        timeout
    ]).then(val=>{
     
        console.log(val);
    }).catch(err=>{
     
        console.log(err);
    })

1.6、promise执行时序的问题

console.log('global start');

setTimeout(()=>{
     
    console.log('setTimeout');
},0)

Promise.resolve()
    .then(()=>{
     
        console.log('promise');
    })
    .then(()=>{
     
        console.log('promise 2');
    })
    .then(()=>{
     
        console.log('promise 3');
    })
    .then(()=>{
     
        console.log('promise 4');
    })

console.log('global end');
// 打印结果如下:
// global start
// global end
// promise   
// promise 2 
// promise 3 
// promise 4 
// setTimeout

这涉及到了宏任务与微任务的概念。
宏任务:可以理解为每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。 浏览器为了让 JS 内部宏任务 与 DOM 操作能够有序的执行,会在一个宏任务执行结束后,在下一个宏任务执行开始前,对页面进行重新渲染。
宏任务包含:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、MessageChannel 等

微任务:可以理解是在当前任务执行结束后需要立即执行的任务。也就是说,在当前任务后,在渲染之前,执行清空微任务。
所以它的响应速度相比宏任务会更快,因为无需等待 UI 渲染。
微任务包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)等

二、Generator

ES6 新引入了 Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案


function * foo2(){
     
    console.log("foo2");
    // yield并不会像return 一样立即结束函数的执行,他只是暂停这个函数的执行,知道下一次去调用生成器的next()方法时,会从yield这个位置继续往下执行
    yield 'foo'
    yield 'foo2'
}
const generator2 = foo2()
console.log(generator2.next()); // { value:'foo' , done:false} done表示这个生成器是否已经全部执行完毕
console.log(generator2.next()); // {value:'foo2' , done:true}
console.log(generator2.next()); // {value:undefined , done:true}


// ====================================== 分割线 ====================================

// 如果传入了参数的话,这个参数会作为 yield语句的返回值
function * foo3(){
     
    // yield并不会像return 一样立即结束函数的执行,他只是暂停这个函数的执行,知道下一次去调用生成器的next()方法时,会从yield这个位置继续往下执行
    const res = yield 'foo'
    console.log(res);

    const res2 = yield 'foo2'
    console.log(res2);
}
const generator3 = foo3()
generator3.next()
// 需要在第一次执行到 yield 关键字之后 ,再次执行时传入参数
// 这个参数会作为 yield的返回值,res就会接收到这个返回值
// 传参的同时,函数继续执行,到下一次yield暂停
console.log(generator3.next("baibai"));
console.log(generator3.next("嘿嘿"));

// ====================================== 分割线 ====================================
// 使用try catch监听异常
function * foo4(){
     
    try{
     
        const res = yield 'foo'
        console.log(res);
    }catch(e){
     
        console.log(e);
    }
}
const generator4 = foo4()
const result = generator4.next()
generator4.next("cool")
generator4.throw(new Error('Generator error'))

三、Gererator联合Promise使用

function ajax(url){
     
    return new Promise(function(resolve,reject){
     
        // throw new Error()
        var xhr = new XMLHttpRequest()
        xhr.open('GET',url)
        xhr.responseType = 'json'
        xhr.onload = function(){
     
            if(this.status == 200){
     
                resolve(this.response)
            }else{
     
                reject(new Error(this.statusText))
            }
        }
        xhr.send()
    })
}

function * main(){
     
    try{
     
        const users = yield ajax('/api/user.json')
        console.log(users);
    
        const list = yield ajax('/api/list.json')
        console.log(list);
    } catch(err){
     
        console.log(err);
    }
    
}

const result = g.next() // 这里会的到一个对象,对象的 value值是一个 Promise对象
result.value.then(data =>{
     
    // 将data作为参数,data会作为yield的返回值,赋值给users,同时函数继续往下执行,result2接收的值为 {value:ajax('/api/list.js') , done:false}
    const result2 = g.next(data)

    if(result2.done) return

    result2.value.then(data=>{
     

        const result3 = g.next(data)

        if(result2.done) return

        result3.value.then(data=>{
     
            g.next(data)
        }
    })
})

优化代码

function handleResult(result){
     
    if(result.done) return;
    result.value.then(data=>{
     
        // 再次执行next,并将执行结果作为参数再次调用 handlerResult方法
        handleResult(g.next(data))
    },err=>{
     
        g.throw(err)
    })
}
handleResult(g.next())

封装

function co(generator){
     
    const g = generator()   

    function handleResult(result){
     
        if(result.done) return;
        result.value.then(data=>{
     
            // 再次执行next,并将执行结果作为参数再次调用 handlerResult方法
            handleResult(g.next(data))
        },err=>{
     
            g.throw(err)
        })
    }

    handleResult(g.next())
}
co(main)

也有已经封装好的更完善的库,例如:https://github.com/tj/co

四、Async Await

// 结合上面的Generator和Promise的联合使用来看,async和await的使用只是改动了部分内容
async function main(){
     
    try{
     
        const users = await ajax('/api/user.json')
        console.log("第一次打印",users);

        const list = await ajax('/api/list.json')
        console.log("第二次打印",list);
    }catch(err){
     
        console.log(err);
    }
}
main()

你可能感兴趣的:(javascript,ajax)