单线程的JavaScript

单线程的JavaScript

  • 同步模式与异步模式
  • 异步编程的几种方式
  • Promise异步方案
  • Generator异步方案
  • async/await语法糖

同步模式

JavaScript在调用栈(call stack)中按顺序执行代码

console.log('global start');
function bar(){
    console.log('bar task');
}
function foo(){
    console.log('foo task');
    bar()
}
foo()
console.log('global end');
// 输出:
// global start
// foo task
// bar task
// global end

异步模式

JavaScript在调用栈(call stack)中按顺序执行代码,异步方法会被放在消息队列(Queue)中等待执行.setTimeout属于web APIs内的异步方法,消息队列中的代码会在event loop(事件循环)刷新后开始执行

console.log('global start');
setTimeout(() => {
    console.log('timer1');
}, 1800);
setTimeout(() => {
    console.log('timer2');
    setTimeout(() => {
        console.log('inner');
    }, 1000);
}, 1000);
console.log('global end');

// 输出
// global start
// global end
// timer2
// timer1
// inner

回调函数

// 回调函数测试 --封装ajax请求
function Aajx(url,callback){
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if(this.readyState == 4){
            if(this.status === 200 || this.status === 304){
                // 成功回调
                callback(this.responseText)
            } else {
                // 失败回调
                callback(new Error('请求失败啦'))
            }
        }
    }
    xhr.open('get',url)
    xhr.send()
}

Aajx('http://192.168.31.243:9099',function(res){
    console.log('成功接收数据:',res)
})

回调地狱: 多个依赖于上一个回调结果的多层回调嵌套称为回调地狱,缺点: 代码不好调试,可读性差.

Promise

// Promise测试
function Ajax(url){
    return new Promise((resolve,reject)=>{
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
            if(this.readyState == 4){
                if(this.status === 200 || this.status === 304){
                    // 成功
                    resolve(this.responseText)
                } else {
                    // 失败
                    reject(new Error('请求失败啦'))
                }
            }
        }
        xhr.open('get',url)
        xhr.send()
    })
}
// 单接口
Ajax('http://192.168.31.243:9099')
.then((res)=>{
    console.log('成功>',res)
})
// 多接口调用
Ajax('http://192.168.31.243:9099')
.then((res)=>{
    console.log('成功>',res)
    Ajax('http://192.168.31.243:9099')
    .then((res2)=>{
        console.log('成功2>',res2)
    })
})

Generator(生成器)

第一个next()会先执行yield前面和里面的代码(不包括赋值),第二个next()会执行上一个yield的赋值操作和第二个yield前面和里面的代码(不包括赋值),以此类推,最后一个next()(此时没有yield)会执行剩余代码.如果yield后是一个异步方法,那么将会接着往下执行,不会等待异步方法执行结束后再执行.

next()的执行返回结果是: {value: “”, done: true},value是yield后的结果(可以是字符串,函数…),done表示执行完当前next是否就生成器内部就都执行结束了,true表示都结束了,false表示还有代码没有运行到.

// 例1: 生成器执行顺序
function* foo(){
    console.log('start code');
    var res1 = yield 'return data1'
    var res2 = yield 'return data2'
    console.log(res1)
    console.log(res2)
    console.log('end code');
}
// var gener = foo() 不会执行foo函数代码
// 调用next() 方法才会调用方法.只会执行到 yield 'return data',并且不会执行赋值操作
var gener = foo()
// 执行第一个yield
console.log(gener.next());
// 执行第二个yield
console.log(gener.next());
// 执行剩余代码
console.log(gener.next());

// 输出结果:
// start code
// {value: "return data1", done: false}
// {value: "return data2", done: false}
// undefined
// undefined
// end code
// {value: undefined, done: true}
// 例2: 第二个next传递的参数是上一个yield赋值的值
function* foo(){
    console.log('start code');
    var res = yield (function aa(){
        return 'aa'
    })()
    console.log(res)
    console.log('end code');
    return '执行结束'
}
// var gener = foo() 不会执行foo函数代码
// 调用next() 方法才会调用方法.只会执行到 yield 后面部分,并且不会执行赋值操作
var gener = foo()
// 执行第一个yield
console.log(gener.next());
// 执行剩余代码,这个next的参数就是上述res的值
console.log(gener.next(999));

// 输出结果:
// start code
// {value: "aa", done: false}
// 999
// end code
// {value: "执行结束", done: true}
// 例3: yield后是异步函数,不会阻塞线程,会先执行外部代码
function* foo(){
    console.log('start code');
    var res = yield Ajax('http://192.168.31.243:9099') // value 是一个promise对象
    console.log(res)
    console.log('end code');
    return '执行结束'
}
// var gener = foo() 不会执行foo函数代码
// 调用next() 方法才会调用方法.只会执行到 yield 后面部分,并且不会执行赋值操作
var gener = foo()
// 执行第一个yield
// 由于Ajax是个异步方法,所以会先执行后续的操作
gener.next().value.then((res)=>{
    console.log(res)
})
// 执行剩余代码,这个next的参数就是上述res的值
console.log(gener.next(999));

// 输出结果:
// start code
// 999
// end code
// {value: "执行结束", done: true}
// res结果
// 例4: 实现多层嵌套请求
function* foo(){
    console.log('start code');
    var res1 = yield Ajax('http://192.168.31.243:9099') // value 是一个promise对象
    console.log(res1)
    var res2 = yield Ajax('http://192.168.31.243:9099') // value 是一个promise对象
    console.log(res2)
    var res3 = yield Ajax('http://192.168.31.243:9099') // value 是一个promise对象
    console.log(res3)
    console.log('end code');
    return '执行结束'
}
// 多层请求的情况 ---使用递归实现
var gener = foo()
// gener.next()
h(gener.next())
function h(nextObj){
    if(nextObj.done) return
    nextObj.value.then(res=>{
        h(gener.next(res))
    })
}

// 输出结果:
// res1结果
// res2结果
// res3结果
// end code

async/await (Generator语法糖)

// 字节跳动面试题
setTimeout(() => {
    console.log('setTimeout');
}, 0);
async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}
console.log('script start')
async1()
console.log('script end')

// 结果
// script start
// async1 start
// async2
// script end
// async1 end
// setTimeout

由于async2()是一个异步方法,所以会先执行后续的代码console.log('script end'),然后等待结束后再执行console.log('async1 end'),这和Generator生成器的原理有关.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值