js中generator的详解

 在解释generator之前,我们先来说下同步/异步以及Promise ,因为generator和Promise有许多类似的地方,Promise是为了解决回调地狱,而Generator 是为了应对异步问题。

1.同步与异步

众所周知,JS是一个单线程语言,同一个时间点只能做一件事。因而所有任务需要排队,只有前面任务结束,才会执行后面的任务。这样带来的问题:如果JS执行时间长,致使页面渲染不连贯,页面卡顿体验不好。为解决这个问题,HTML5出台了Web Worker标准,支持JavaScript脚本生成多个线程,这就是JS中的同步和异步。异步任务又分为宏任务(javascript代码,setTimeout,setinterval,setimmediate,io操作,ui render,异步ajax)和微任务(promise then,catch,finnaly,async/await , process.nextTick, Object.observe, mutationobserve),其中异步任务属于耗时的任务。

promise经典面试题:(微任务优先执行)

setTimeout(function () {
 console.log('1');
})
new Promise(function (resolve) {
 console.log('2');
 resolve();
}).then(function () {
 console.log('3');
})
console.log('4');
//打印顺序 2 4 3 1

执行顺序:

let axios = require('axios');
let fs = require('fs');
console.log('begin');
fs.readFile('1.txt',(err,data)=>{
	console.log('fs');
});
axios.get('https://api.muxiaoguo.cn/api/xiaohua?api_key=fd3270a0a9833e20').then(res=>{
	console.log('axios');
});
setTimeout(()=>{
	console.log('setTimeout')
},0);
setImmediate(()=>{
  console.log('setImmediate');
});
(async function (){
	console.log('async')
})();
console.log('end');
//执行结果:begin async end setTimeout setImmediate fs axios

当setTimeout延迟时间也是0毫秒时,谁在前面就先执行谁。此外如果setTimeout延迟时间不是0毫秒,它的执行顺序会在 i/o 操作之后。 

2.什么是generator

对于普通函数而言,它会立即执行,而Generator 函数特性是可以停下,不会立即执行,正是这样的特性,它能很好处理异步问题。

定义一个 Generator 函数和定义一个普通函数是类似的,不同之处在于它在 function 和函数名之间有一个*号。

Generator 函数返回是一个迭代器对象,需要通过 xxx.next 方法来完成执行。在调用 generator 函数时,它只是进行实例化工作,它没有让函数体里面的代码执行,需要通过 next 方法来让它执行,如下面:

function* gen() {
    console.log(1)
}
// 定义迭代器对象
const iterator = gen()
iterator.next() // 执行这一局代码,1才会被打印

当 next 方法执行时遇到了yield时 会停止,直到你再次调用 next 方法。如下:

function* genar() {
    yield 1
    console.log('A')
    yield 2
    console.log('B')
    yield 3
    console.log('C')
    return 4
}

// 定义迭代器对象
const iterator = genar()
iterator.next() // 执行 genar 函数,打印为空,遇到 yield 1 停止执行
iterator.next() // 继续执行函数,打印 A,遇到 yield 2 停止执行
iterator.next() // 继续执行函数,打印 B,遇到 yield 3 停止执行
iterator.next() // 继续执行函数,打印 C

3.generator能做什么

我们说 Generator 能够把异步变同步,是因为 Generator 函数中我们只需要写同步代码就可以,真正执行异步操作的是迭代器对象。

在复杂的业务逻辑中,大量使用迭代器对象来执行异步操作,会使得代码变得很不优雅,于是 ES7 中就推出了 async await 方案来实现异步变同步。在 async await 方案中可以只书写同步代码,真正的异步操作被封装在底层,这样写使得代码变优雅了很多。

4.generator的应用场景

使用generator来完成异步网络请求,它还是要利用到promise, 优雅的写法,当有多个网络请求时,异步操作部分的代码会变得非常复杂,所以我们可以通过 co 库中的迭代函数来改写一下:

// 使用generator来完成异步网络请求,利用promise

// 模拟网络请求
function request(num) {
    return new Promise((resolve, reject) => {
        return setTimeout(() => {
            resolve(++num)
        }, 1000);
    })
}

// generator函数中的代码,发起的网络请求它就类似于同步写法
function* gen(num) {
    // yield右侧是一个promise对象
    let r1 = yield request(10)
    console.log('r1', r1);

    let r2 = yield request(r1)
    console.log('r2', r2);

    let r3 = yield request(r2)
    console.log('r3', r3);

    let r4 = yield request(r3)
    console.log('r4', r4);

    let r5 = yield 'ok'
    console.log('r5', r5);
}


// 通过co库实现
function co(generator, ...params) {
    const iterator = gen(...params)

    // 迭代函数
    const next = n => {
        let { value, done } = iterator.next(n)
        // 判断一下value它是一个promise对象,如果不是promise对象则需要手动转为promise对象,或抛异常
        if (value != undefined && typeof value.then != "function") {
            throw new Error('必须为promise对象')
            // value = Promise.resolve(value)
        }
        if (done) return;
        // value.then(ret => next(ret))
        value.then(next)
    }
    next(0)
}

co(gen, 100)


 

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weifont

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值