美文网首页
笔记二:JavaScript异步编程

笔记二:JavaScript异步编程

作者: 油菜又矮吹 | 来源:发表于2020-07-23 16:03 被阅读0次

    文章内容输出来源:拉勾教育大前端高薪训练营 和自我总结

    JS是单线程语言

    • JS执行环境中负责执行代码的线程只有一个
    • 执行任务的模式有两种:同步模式(Synchronous)、异步模式(Asynchronous)
      优点:更安全更简单
      缺点:遇到耗时任务,之后任务需要排队,会出现拖延假死的现象
      这里的同步/异步是指运行环境提供的API是以同步或者异步模式的方式工作

    同步模式(Synchronous)

    同步执行模式指的是代码中任务依次执行,后一个任务必须等前一个任务执行完成后才能开始执行,程序执行顺序和代码编写顺序一致,这种方式相对简单,在单线程情况,大多数情况下都是以同步模式执行

    异步模式(Asynchronous)

    异步执行模式不会等待这个任务的结束才开始下一个任务,开启过后就立即往后执行下一个任务,后续逻辑一般会通过回调函数的方式定义
    异步模式的重要性:没有异步模式,单线程的JavaScript语言就无法同时处理大量耗时任务

    回调函数()

    回调函数是所有异步实现的根基

    Promise

    基本用法

    //Promise 基本示例
    
    const promise = new Promise(function (resolve, reject) {
        //这里用于兑现承诺
        // resolve(100) //  承诺达成
    
        reject(new Error('promise rejected'))   //承诺失败
    })
    
    promise.then(function (value) {
        console.log('resolved', value)
    },function (error) {
        console.log('rejected',error)
    })
    
    console.log('end')   // 先打印出end,再打印Error
    

    Promise封装ajax

    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()
        })
    }
    
    ajax('./api/users.json').then(function (res) {
        console.log(res)
    },function (error) {
        console.log(error)
    })
    

    Promise通过链式调用避免回调嵌套

    • Promise对象的then方法会返回一个全新的Promise对象
    • 后面的then方法就是在为上一个then返回的Promise注册回调
    • 前面then方法中回调函数的返回值会作为后面then方法回调的参数
    • 如果回调返回的是Promise,那后面then方法的回调会等待它的结束
    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()
        })
    }
    
    var promise = ajax('./api/users.json')
    
    // then 方法返回一个全新的promise对象
    var promise2 = promise.then(function (res) {
        console.log(res)
    }, function (error) {
        console.log(error)
    })
    
    console.log(promise2 === promise) // false
    
    // 每一个then方法都是在为上一个then方法添加状态明确过后的回调
    ajax('./api/users.json')
        .then(function (value) {
            console.log(111)
            return ajax('./api/users.json')
        }) // => Promise
        .then(function (value) {
            console.log('shuju1', value) //Array(2)
            console.log(222)
            return 'foo'
        }) // => Promise
        .then(function (value) {
            console.log(333)
            console.log('shuju2', value)  // foo
        }) // => Promise
        .then(function (value) {
            console.log(444)
            console.log('shuju3', value)  //undefined
        }).catch(function onRejected(error) {
        console.log('onRejected', error)
    })
    

    其中catch也是then的别名

    .catch(function onRejected(error) {
      console.log('onRejected', error)
    })
    // 就相当于
    .then(undefined, function (value) {
      console.log(444)
      console.log('数据3', value)
    })
    

    then中的第二个参数是reject函数,catch中的参数也是reject函数,但是作用不太相同,then中的reject不能捕获到第一个参数中的resolve中的异常,但是catch由于链式作用,能捕获到前面任意处的异常

    ajax('/api/users.json')
    .then(function (value) {
      console.log(111)
      return ajax('./api/users.json') // 这个异常无法捕获
    }, function onRejected (e){
      console.log('reject', e)
    }) 
    

    推荐使用catch捕获异常,可以捕获整个promise链条上的异常:

    ajax('/api/users.json')
    .then(function (value) {
      console.log(111)
      return ajax('./api/users.json') // 这个异常无法捕获
    }) .catch(function onRejected (e){
      console.log('reject', e)
    })
    

    还可以全局捕获异常:
    浏览器中,在window对象上注册事件:

    window.addEventListener('unhandledrejection', event => {
      const {reason, promise} = event
      console.log(reason, promise)
      // reason => Promise 失败原因,一般是一个错误对象
      // promise => 出现异常的Promise对象
      event.preventDefault()
    }, false)
    

    node中:

    process.on('unhandledRejection', (reason, promise) => {
      const {reason, promise} = event
      console.log(reason, promise)
      // reason => Promise 失败原因,一般是一个错误对象
      // promise => 出现异常的Promise对象
    })
    

    尽量在代码中明确捕获每一个可能的异常,而不是丢给全局处理。

    Promise静态方法

    Promise.resolve() 快速的把一个值转化为Promise对象
    Promise.resolve('foo')
        .then(value=>{
            console.log(value)
        })
    
    //Promise.resolve('foo') 等价于
    new Promise((resolve, reject) => {
        resolve('foo')
    })
    
    var promise = ajax('/api/users.json')
    var promise = Promise.resolve(promise)
    console.log(promise === promise2)  //true
    
    //带then方法的对象,就是实现了thenable接口,可以被then的对象
    Promise.resolve({
        then:((onFulfilled, onRejected) => {
            onFulfilled('foo')
        })
    }).then(value => {
        console.log(value) //foo
    })
    
    Promise.reject(err) 传入的对象为失败的原因
    Promise.reject('anything')
    .catch(err=>{
        console.log(err)  //anything
    })
    

    Promise并行执行

    Promise.all() 等待所有任务成功结束了,才算结束
    var promise = Promise.all([
        ajax('/api/users.json'),
        ajax('/api/posts.json'),
    ])
    
    //只有promise里面的每一个任务都执行城关了才进入resolve
    //如果其中任何一个失败了,都会进入catch
    promise.then(values => {
        console.log(values)
    //   Array(2)
    // 0:
    // username: "yibo"
    // __proto__: Object
    // 1:
    // name: "jiailing"
    }).catch(err => {
        console.log(err)
    })
    
    ajax('/api/urls.json')
        .then(value => {
            const urls = Object.values(value)
            const tasks = urls.map(url => ajax(url))
            return Promise.all(task)
        }).then(values => {
            console.log(values)
    })
    
    Promise.race() 只会等待第一个结束的任务
    const request = ajax('/api/posts.json')
    const timeout = new Promise((resolve, reject) => {
        setTime(() => {
            reject( new Error('timeout'))
        }, 500)
    })
    
    Promise.race([request, timeout])
    .then(value => {
        console.log(value)
    }).catch(err => {
        console.log(err)
    })
    //试下ajax请求超时控制的一种方式
    

    微任务

    即使Promise中没有任何异步操作,它的回调函数仍然会进入到回调队列中排队。必须等待所有同步代码执行完毕后,Promise中的代码才会被调用。

    JS回调队列中的任务称之为宏任务,而宏任务执行过程中可以临时加上一些额外需求,可以选择作为一个新的宏任务进入到队列中排队(如:setTimeout),也可以作为当前任务的微任务。直接在当前任务结束后立即执行

    Promise的回调会作为微任务执行。微任务的目的是为了提高整体的响应能力,目前绝大多数异步调用都是作为宏任务执行,Promise、MutationObserver、process.nextTick是作为微任务在本轮调用的末尾执行

    console.log('global')  //第一个打印
    
    Promise.resolve()
        .then(() => {
            console.log('promise1') //第3个打印
        })
        .then(() => {
            console.log('promise2') //第4个打印
        })
        .then(() => {
            console.log('promise3') //第5个打印
        })
    
    console.log('global end') //第2个打印
    

    setTimeout属于宏任务

    console.log('global')  //第一个打印
    
    setTimeout(()=>{
        console.log('last')  //第6个打印
    },0)
    
    Promise.resolve()
        .then(() => {
            console.log('promise1') //第3个打印
        })
        .then(() => {
            console.log('promise2') //第4个打印
        })
        .then(() => {
            console.log('promise3') //第5个打印
        })
    
    console.log('global end') //第2个打印
    
    console.log('global start') //第1个打印
    

    Generator异步方案

    Generator的基本使用

    生成器函数会返回一个生成器对象,调用这个生成器对象的next方法,才会让函数体执行,一旦遇到了yield关键词,函数的执行则会暂停下来,yield后面的值作为next函数的结果返回,如果继续调用函数的next函数,则会在上一次暂停的位置继续执行,直到函数体执行完成,next返回的对象的done就会变成true

    function * fn() {
        console.log(111)
        yield 100
        console.log(222)
        yield 200
        console.log(333)
        yield 300
    }
    
    const generator = fn()
    console.log(generator.next())
    // 111
    // {value:100,done:false}
    console.log(generator.next())
    // 222
    // {value:200,done:false}
    console.log(generator.next())
    // 333
    // {value:300,done:false}
    

    Generator实现异步

    注意:generator.next(value)中,next传入的参数会作为上一次yield的返回值。

    function ajax(url){
        return new Promise((resolve, reject) => {
            var xhr = 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() {
        const users = yield ajax('/api/user.json')
        console.log(users)
        
        const posts = yield ajax('/api/posts.json')
        console.log(posts)
        
        const urls = yield ajax('/api/urls.json')
        console.log(urls)
    }
    
    //调用生成器函数得到一个生成器对象
    const generator = main()
    
    //递归实现generator.next()的调用,知道done为true终止
    function dfs(value) {
        const result = generator.next(value)
        if(result.done) return
        result.value.then(data => {
            console.log(data)
            dfs(data)
        })
    }
    
    dfs()
    // 打印结果
    // Generator实现异步.js:35 {username: "yibo"}
    // Generator实现异步.js:19 {username: "yibo"}
    // Generator实现异步.js:35 {posts: "jiailing"}
    // Generator实现异步.js:22 {posts: "jiailing"}
    // Generator实现异步.js:35 {posts: "/api/posts.json", users: "/api/users.json"}
    // Generator实现异步.js:25 {posts: "/api/posts.json", users: "/api/users.json"}
    

    封装生成器函数执行器co

    function ajax(url){
        return new Promise((resolve, reject) => {
            var xhr = 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() {
        const users = yield ajax('/api/user.json')
        console.log(users)
    
        const posts = yield ajax('/api/posts.json')
        console.log(posts)
    
        const urls = yield ajax('/api/urls.json')
        console.log(urls)
    }
    
    //调用生成器函数得到一个生成器对象
    const generator = main()
    
    //封装了一个生成器函数的执行器
    function co(main) {
        //调用生成器函数得到一个生成器对象
        const generator = main()
        
        //递归实现generator.next()的调用,直到done为true终止
        function handleResult(result){
            if(result.done) return 
            result.value.then(data => {
                console.log(data)
                handleResult(generator.next(data))
            },err => {
                generator.throw(err)
            })
        }
        
        handleResult((generator.next()))
    }
    
    co(main)
    // 打印结果
    // Generator实现异步.js:42 {username: "yibo"}
    // Generator实现异步.js:20 {username: "yibo"}
    // Generator实现异步.js:42 {posts: "jiailing"}
    // Generator实现异步.js:23 {posts: "jiailing"}
    // Generator实现异步.js:42 {posts: "/api/posts.json", users: "/api/users.json"}
    // Generator实现异步.js:26 {posts: "/api/posts.json", users: "/api/users.json"}
    

    Async/Await 语法糖

    await关键词只能出现在async函数中。

    function ajax(url){
        return new Promise((resolve, reject) => {
            var xhr = 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()
        })
    }
    async function main(){
        try{
            const users = await ajax('/api/users.json')
            console.log(users)
    
            const posts = await ajax('/api/posts.json')
            console.log(posts)
    
            const urls = await ajax('/api/urls.json')
            console.log(urls)
        }catch (e) {
            console.log(e)
        }
    }
    
    main()
    
    // async-await.js:20 {username: "yibo"}
    // async-await.js:23 {posts: "jiailing"}
    // async-await.js:26 {posts: "/api/posts.json", users: "/api/users.json"}
    

    相关文章

      网友评论

          本文标题:笔记二:JavaScript异步编程

          本文链接:https://www.haomeiwen.com/subject/fggrkktx.html