js异步从入门到摒弃(四)- Generator 封装异步使命

在之前的文章引见了传统异步的完成计划,本文将引见ES6中的一种全新的异步计划–Generator函数。

generator简介

简朴引见一下generator的道理和语法,(更细致内容请看ECMAScript 6 入门,本文只引见和异步相干的中心内容)

基础语法

经由过程一个简朴的例子来相识generator函数

function* MyGenerator() {
    yield 'yield的寄义是:实行此处时,停息实行当前函数'
    yield '停息以后的函数可以用next要领继承实行'
    return '碰到return以后会真正完毕,done会变成true'
}

const gen = MyGenerator() //实行后返回的是一个指针,可以应用该指针实行next要领
console.log(gen.next()) // {value: "yield的寄义是:实行此处时,停息实行当前函数“, done: false}
console.log(gen.next())// {value: "停息以后的函数可以用next要领继承实行", done: false}
console.log(gen.next())// {value: "碰到return以后会真正完毕,done会变成true", done: true}

数据交互

数据交互指的是可以经由过程yield把当前实行的效果传出,也可以运用next把外部参数传入

    function* MyGenerator(){
        const result = yield '传出的数据'
        return result
    }
    const gen = MyGenerator()
    console.log(gen.next())// generatorAndAsync2.html:19 {value: "传出的数据", done: false}
    console.log(gen.next('传入的数据'))// {value: "传入的数据", done: true}

交互的参数范例固然也可以是函数

    function sayHi(){
        console.log('hi');
    }
    
    function* MyGenerator(){
        const result = yield sayHi()
        return 
    }
    
    const gen = MyGenerator()
    const result = gen.next()// {value: sayHi, done: false}
    result.value() // 一般输出'hi'

具有以上最中心的两个特征以后,generator就可以举行异步操纵封装。

异步使命封装

起首,连系异步使命的特性以及前文提到的genrator函数的特征,提炼出运用generator封装异步操纵的中心思绪:

  1. 在异步使命实行时,运用yield交出实行权
  2. 在异步使命完毕后,运用next交还实行权

起步

从一个最简朴的例子最先:

// 1. 起首写一个异步使命,在一秒后返回特定字符串
function asyncTask(callback){
    setTimeout(()=>{
        callback('Hello Leo')
    }, 1000)
}

// 2. 接下来写出希冀实行的递次
function* runTask() {
    let text = yield asyncTask
    console.log(text) // 我们希冀这里一般输出Hello Leo
}
// 3. 依据希冀值实行函数
const gen = runTask()// 此时实行权已交出
gen.next().value(function (text) {// 实行asyncTask并传入callback ,症结点在于在callback里挪用next交还实行权
    gen.next(text)
}) 

起首,这段代码虽然很粗拙,然则已反应了运用generator封装异步使命的中心头脑。最直观的受益就是:runTask的内容看起来很像同步代码,条理清楚,很合适浏览。

然则上面第3部份关于实行的代码很不天真,我们不能每次都这么写一段,因而接下来的目的就是完成一个使命实行器

自动使命实行器

一样的,先思索中心的思绪:要想让某个generator函数自动实行,不过就是一个while轮回:

1. 假如当前yield返回值的done属性为true,申明使命已实行完成;
2. 假如当前yield返回值的done属性为false,申明使命还未实行完成,则继承实行next,同时要注意参数通报

依据剖析完成以下的实行器:

function autoExecute(task) {
    const gen = task()
    let result = gen.next()
    while(true){
        if(result.done){
            break // 实行完毕
            return 
        }
        console.log(result.value)//为了便于视察 我们加上console.log
        result = gen.next(result.value) // 每次都应该重写result 猎取最新效果
    }
}

function* simpleTask(){
    yield 1
    yield 2
    yield 3
    return 
}

autoExecute(simpleTask)// 测试我们写的自动实行器 可以准确输出123

上面的实行器已有了雏形,然则关于前面例子中,result.value为函数的状况还没有处置惩罚,因而还需要轻微补充:

function isFunction(source){
     return Object.prototype.toString.call(source) === "[object Function]"
}

function autoExecute(task) {
    const gen = task()
    let result = gen.next()
    let isRuningAsync = false // 因为加入了异步处置惩罚,所以要增添一个标志位防止反复进入轮回体
    while (!isRuningAsync) {
        if (result.done) {
            return
        }
        console.log(result.value)

        /* start 补充的处置惩罚函数 */
        if (isFunction(result.value)) {
            isRuningAsync = true
            const callback = (arg) => {
                result = gen.next(arg) // 中心代码
                isRuningAsync = false
            }
            result.value(callback)
            /* end 补充的处置惩罚函数 */
        } else {
            result = gen.next(result.value)
        }
    }
}
autoExecute(runTask) // 试着用这个自动实行器实行之前的异步使命

上面这个自动实行器,就完成了generator对异步使命的封装。

总结

本文扼要引见了generator函数的一些特征,重点在于申明怎样运用generator函数对异步使命举行封装,从而可以让异步代码编写的越发清楚。

再次强调:用generator函数对异步使命封装的头脑是很明白的–掌握 Generator 函数的流程,在恰当的机遇吸收和交还顺序的实行权,然则详细的完成体式格局并不唯一,比方本文用的是最简朴直接的回调函数体式格局,在阮一峰先生的《es6入门》教程里,也有运用thunk思绪来解说的部份,可以自行查阅。

    原文作者:安歌
    原文地址: https://segmentfault.com/a/1190000019298832
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞