Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

好未来技术分享2019.12.23--用Generator实现异步 #79

Open
zhangyanan0525 opened this issue Dec 21, 2019 · 1 comment
Open

好未来技术分享2019.12.23--用Generator实现异步 #79

zhangyanan0525 opened this issue Dec 21, 2019 · 1 comment

Comments

@zhangyanan0525
Copy link
Owner

zhangyanan0525 commented Dec 21, 2019

前言

这次原定主题是《异步模式的发展》应该会分为四部分:

  • 《回调》
  • 《promise》
  • 《用Generator实现异步》
  • 《Generator与promise联合 && async/await》
    我觉得大家如果不常用Generator的语法的话,可能会忘记了,而且Generator确实是值得拿出来单独分享一次。这次先分享这个《用Generator实现异步》。分享过之后才可能串起来js的异步模式是怎么发展的。

用Generator实现异步

语法回顾

迭代器(Iterator)

我们先说什么是迭代器:凡是符合迭代器协议的,都叫迭代器。
那么什么是迭代器协议?我从mdn上截图了。
屏幕快照 2019-12-21 下午9 52 39
意思就是说:
在 JavaScript 中 迭代器是一个对象,它提供了一个next() 方法,用来返回一个结果对象。这个结果对象包含两个属性:done和 value。
让我们手撕一个迭代器:

let obj = {
    count: 1,
    next(){
        const done = this.count>3
        const value = this.count>3?undefined:this.count++
        return { value, done }
    }
}

MDN上还有一句话我觉得对理解定义有帮助:It is not possible to know reflectively whether a particular object implements the iterator protocol

生成器(Generator)

The Generator object is returned by a generator function and it conforms to both the iterable protocol and the iterator protocol.
那什么是generator function?
Generator functions are written using the function* syntax. When called initially, generator functions do not execute any of their code, instead returning a type of iterator called a Generator.
function* 这种语法是生成器函数。我们调用了之后会返回一个Generator。这个Generator是个iterator。
生成器函数每次被调用都会生成一个新的Generator,这个Generator只能被迭代一次。
注意:不能用箭头函数创建生成器
让我们写一个generator function:

function * foo (){
    yield 1
    yield 2
    yield 3
}
let g = foo()
g.next() -->  {value: 1, done: false}
g.next() -->  {value: 2, done: false}
g.next() -->  {value: 3, done: false}
g.next() -->  {value: undefined, done: true}
g.next() -->  {value: undefined, done: true}
g.next() -->  {value: undefined, done: true}
g.next() -->  {value: undefined, done: true}
g.next() -->  {value: undefined, done: true}

我们可以看到以上代码非常容易的就做到了暂停执行,然后通过调用next又能继续执行了。让我们来给next传一点参数进去。

function * foo (){
    let a = yield 1
    console.log(a)
    let b = yield 2
    console.log(b)
    let c = yield 3
    console.log(c)
}
let g = foo()
g.next(111) -->  {value: 1, done: false} //注意!第一次next传参是没用的!
g.next(222) -->  222 {value: 2, done: false}
g.next(333) -->  333 {value: 3, done: false}
g.next(444) --> 444  {value: undefined, done: true}
g.next(555) -->  {value: undefined, done: true}
g.next(666) -->  {value: undefined, done: true}
g.next(777) -->  {value: undefined, done: true}
g.next(888) -->  {value: undefined, done: true}
g.next(999) -->  {value: undefined, done: true}

158961576995630_ pic_hd
这样我们就看到了生成器的另一个很有用的功能,我们可以通过next给传参数,进行异步填充值

实现简单的异步

想象下有下面这样的代码:


function makeAjaxCall(url,cb) {    
    // do some ajax fun    
    // call `cb(result)` when complete
}
makeAjaxCall( "http://some.url.1", function(result1){    

    makeAjaxCall( "http://some.url.2/?id=" + result1.id, function(result2){        
   
        console.log( "The value you asked for: " +result2 );   
     });
} );

使用generator(不需要额外的修饰)来表达相同的程序,你可以这么写:

function request(url) {    
    // this is where we're hiding the asynchronicity,    
    // away from the main code of our generator    
    // `it.next(..)` is the generator's iterator-resume    
    // call    
    makeAjaxCall( url, function(response){        
        it.next( response );   
    } );    
    // Note: nothing returned here!
}
function *main() {    
    var result1 = yield request( "http://some.url.1" );    
    
    var result2 = yield request( "http://some.url.2?id=" + result1.id );    

    console.log( "The value you asked for: " + result2 );
}
var it = main();
it.next(); // get it all started

我写了一个setTimeOut模拟的,可能会大家的理解有帮助。

function request(url) {    
    setTimeout(() => {
        it.next( url );
    }, 2000);  
}
function *main() {    
    var result1 = yield request( "url1" );  

    console.log(result1)  
    
    var result2 = yield request( "url2" + result1 );    

    console.log( "The value you asked for: " + result2 );
}
var it = main();
it.next()
@zhangyanan0525
Copy link
Owner Author

zhangyanan0525 commented Dec 21, 2019

举一个例子:
现在我们的请求结果是有缓存的。判断缓存存在,就使用缓存,缓存不存在,才会去发送接口请求。
然后我们改下以上的代码

var cache = {};
function request(url) {   
   if (cache[url]) {
        // "defer" cached response long enough for current        
        // execution thread to complete        
       setTimeout( function(){            
           it.next( cache[url] ); 
         }, 0 );    
    }    
    else {        
        makeAjaxCall( url, function(res){            
            cache[url] = res;            
            it.next( res );        
        } );    
   }
}

看见了吧!?main逻辑(指我们的流控制)相比上面的没有缓存的版本,不需要做任何改变。这是把异步实现的细节抽象出来的真正力量。
#47

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols

@zhangyanan0525 zhangyanan0525 pinned this issue Dec 21, 2019
@zhangyanan0525 zhangyanan0525 unpinned this issue Dec 21, 2019
@zhangyanan0525 zhangyanan0525 pinned this issue Dec 22, 2019
@zhangyanan0525 zhangyanan0525 unpinned this issue Dec 29, 2019
@zhangyanan0525 zhangyanan0525 changed the title 好未来技术分享2019.12.23 好未来技术分享2019.12.23--用Generator实现异步 Dec 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant