es6 宏观异步和微观异步_与ES6生成器异步

es6 宏观异步和微观异步

Now that you've seen ES6 generators and are more comfortable with them, it's time to really put them to use for improving our real-world code.

既然您已经了解了ES6生成器并且对它们更加满意 ,那么现在该是将它们真正用于改进我们的真实代码的时候了。

The main strength of generators is that they provide a single-threaded, synchronous-looking code style, while allowing you to hide the asynchronicity away as an implementation detail. This lets us express in a very natural way what the flow of our program's steps/statements is without simultaneously having to navigate asynchronous syntax and gotchas.

生成器的主要优点在于,它们提供了单线程,看起来具有同步性的代码样式, 同时允许您将异步性隐藏为实现细节 。 这使我们可以非常自然地表达程序的步骤/语句的流程,而不必同时浏览异步语法和陷阱。

In other words, we achieve a nice separation of capabilities/concerns, by splitting up the consumption of values (our generator logic) from the implementation detail of asynchronously fulfilling those values (the next(..) of the generator's iterator).

换句话说,通过将值(我们的生成器逻辑)的消耗与异步实现这些值的实现细节next(..)生成器的迭代器的next(..) 分开 ,我们可以很好地实现功能/关注点的分离。

The result? All the power of asynchronous code, with all the ease of reading and maintainability of synchronous(-looking) code.

结果? 异步代码的所有功能,以及同步(外观)代码的所有易读性和可维护性。

So how do we accomplish this feat?

那么我们如何完成这项壮举呢?

最简单的异步 (Simplest Async)

At its most simple, generators don't need anything extra to handle async capabilities that your program doesn't already have.

最简单的说,生成器不需要任何额外的功能来处理程序尚不具备的异步功能。

For example, let's imagine you have this code already:

例如,假设您已经有以下代码:

function makeAjaxCall(url,cb) {
    // do some ajax fun
    // call `cb(result)` when complete
}

makeAjaxCall( "http://some.url.1", function(result1){
    var data = JSON.parse( result1 );

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


To use a generator (without any additional decoration) to express this same program, here's how you do it:

要使用生成器(不带任何其他修饰)来表示相同的程序,请按以下步骤操作:

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 data = JSON.parse( result1 );

    var result2 = yield request( "http://some.url.2?id=" + data.id );
    var resp = JSON.parse( result2 );
    console.log( "The value you asked for: " + resp.value );
}

var it = main();
it.next(); // get it all started


Let's examine how this works.

让我们研究一下它是如何工作的。

The request(..) helper basically wraps our normal makeAjaxCall(..) utility to make sure its callback invokes the generator iterator's next(..) method.

request(..)帮助程序基本上包装了正常的makeAjaxCall(..)实用程序,以确保其回调调用生成器迭代器的next(..)方法。

With the request("..") call, you'll notice it has no return value (in other words, it's undefined). This is no big deal, but it's something important to contrast with how we approach things later in this article: we effectively yield undefined here.

通过request("..")调用,您会发现它没有返回值 (换句话说,它是undefined )。 这没什么大不了的,但是与本文后面的处理方式形成对比是很重要的:我们在这里有效地yield undefined

So then we call yield .. (with that undefined value), which essentially does nothing but pause our generator at that point. It's going to wait until the it.next(..) call is made to resume, which we've queued up (as the callback) to happen after our Ajax call finishes.

因此,我们称之为yield .. (具有undefined值),它实际上什么也没做,只是在那一点上暂停了生成器。 它要等到it.next(..)调用恢复后,我们才排队(作为回调)在Ajax调用完成之后发生。

But what happens to the result of the yield .. expression? We assign that to the variable result1. How does that have the result of the first Ajax call in it?

但是yield ..表达的结果会怎样? 我们将其分配给变量result1 。 第一次调用Ajax的结果如何?

Because when it.next(..) is called as the Ajax callback, it's passing the Ajax response to it, which means that value is getting sent back into our generator at the point where it's currently paused, which is in the middle of the result1 = yield .. statement!

因为当将it.next(..)称为Ajax回调时,它会将Ajax响应传递给它,这意味着该值将在当前暂停的点(即中间)被发送回生成器。 result1 = yield ..语句!

That's really cool and super powerful. In essence, result1 = yield request(..) is asking for the value, but it's (almost!) completely hidden from us -- at least us not needing to worry about it here -- that the implementation under the covers causes this step to be asynchronous. It accomplishes that asynchronicity by hiding the pause capability in yield, and separating out the resume capability of the generator to another function, so that our main code is just making a synchronous(-looking) value request.

那真的很酷而且超级强大。 从本质上讲, result1 = yield request(..)询问值 ,但是(几乎!)对我们来说是完全隐藏的-至少我们在这里不需要担心-掩盖之下的实现会导致这一步是异步的。 它通过隐藏yield暂停功能,然后将生成器的恢复功能分离到另一个函数中来实现这种异步性,因此我们的主要代码只是发出一个同步(看上去)值请求

The exact same goes for the second result2 = yield result(..) statement: it transparently pauses & resumes, and gives us the value we asked for, all without bothering us about any details of asynchronicity at that point in our coding.

第二个result2 = yield result(..)语句完全相同:它透明地暂停和恢复,并为我们提供了所要求的值,而这一切都不会影响我们那时编码中异步性的任何细节。

Of course, yield is present, so there is a subtle hint that something magical (aka async) may occur at that point. But yield is a pretty minor syntactic signal/overhead compared to the hellish nightmares of nested callbacks (or even the API overhead of promise chains!).

当然, yield存在,所以一种微妙的暗示神奇的东西(也称为异步), 在该点发生 。 但是,与嵌套回调的噩梦相比,甚至与承诺链的API开销相比, yield都是一个很小的语法信号/开销。

Notice also that I said "may occur". That's a pretty powerful thing in and of itself. The program above always makes an async Ajax call, but what if it didn't? What if we later changed our program to have an in-memory cache of previous (or prefetched) Ajax responses? Or some other complexity in our application's URL router could in some cases fulfill an Ajax request right away, without needing to actually go fetch it from a server?

还请注意,我说“可能会发生”。 就其本身而言,这是一件非常强大的事情。 上面的程序总是进行异步Ajax调用,但是如果没有,该怎么办? 如果以后我们将程序更改为具有先前(或预取)Ajax响应的内存缓存,该怎么办? 或在我们的应用程序的URL路由器其他一些复杂性可能会在某些情况下,完成一个Ajax请求向右走 ,而不需要真正去从服务器上获取呢?

We could change the implementation of request(..) to something like this:

我们可以将request(..)的实现更改为以下形式:

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(resp){
            cache[url] = resp;
            it.next( resp );
        } );
    }
}


Note: A subtle, tricky detail here is the need for the setTimeout(..0) deferral in the case where the cache has the result already. If we had just called it.next(..) right away, it would have created an error, because (and this is the tricky part) the generator is not technically in a paused state yet. Our function call request(..) is being fully evaluated first, and then the yield pauses. So, we can't call it.next(..) again yet immediately inside request(..), because at that exact moment the generator is still running (yield hasn't been processed). But we can call it.next(..) "later", immediately after the current thread of execution is complete, which our setTimeout(..0) "hack" accomplishes. We'll have a much nicer answer for this down below.

注意:在这里,一个微妙而棘手的细节是在缓存已经有结果的情况下需要setTimeout(..0)延迟。 如果我们刚刚叫it.next(..)马上,它会产生一个错误,因为(这是棘手的部分)发电机组没有技术上处于暂停状态 。 我们的函数调用request(..)被完全第一评价,然后将yield暂停。 所以,我们不能称之为it.next(..)再次立即里面request(..)因为就在那个时候,发电机仍在运行( yield尚未处理)。 但是,我们可以在当前执行线程完成后立即将其称为“ it.next(..) ”“稍后”,这是我们的setTimeout(..0) “ hack”完成的。 下面我们将对此提供更好的答案。

Now, our main generator code still looks like:

现在,我们的主要生成器代码仍然如下所示:

var result1 = yield request( "http://some.url.1" );
var data = JSON.parse( result1 );
..


See!? Our generator logic (aka our flow control) didn't have to change at all from the non-cache-enabled version above.

看到!? 我们的发电机逻辑(又名我们的流量控制 )没得变化从上面的启用非缓存版本。

The code in *main() still just asks for a value, and pauses until it gets it back before moving on. In our current scenario, that "pause" could be relatively long (making an actual server request, to perhaps 300-800ms) or it could be almost immediate (the setTimeout(..0) deferral hack). But our flow control doesn't care.

*main()的代码仍然只要求一个值,并暂停直到它取回之前才继续。 在我们当前的情况下,“暂停”可能相对较长(发出实际的服务器请求,可能需要300-800ms),或者几乎是立即发生的( setTimeout(..0)延迟hack)。 但是我们的流量控制不在乎。

That's the real power of abstracting away asynchronicity as an implementation detail.

这是将异步抽象为实现细节的真正力量

更好的异步 (Better Async)

The above approach is quite fine for simple async generators work. But it will quickly become limiting, so we'll need a more powerful async mechanism to pair with our generators, that's capable of handling a lot more of the heavy lifting. That mechanism? Promises.

对于简单的异步生成器而言,上述方法非常好。 但是它将很快变得有限,因此我们需要一个更强大的异步机制来与我们的生成器配对,从而能够处理更多的繁重工作。 那个机制? 承诺

If you're still a little fuzzy on ES6 Promises, I wrote an extensive 5-part blog post series all about them. Go take a read. I'll wait for you to come back. <chuckle, chuckle>. Subtle, corny async jokes ftw!

如果您仍然对ES6 Promises感到有些困惑,我撰写了一个由5部分组成的博文,涉及这些内容。 去看书吧 我你回来。 <轻笑,轻笑>。 狡猾的老套异步笑话!

The earlier Ajax code examples here suffer from all the same Inversion of Control issues (aka "callback hell") as our initial nested-callback example. Some observations of where things are lacking for us so far:

与我们最初的嵌套回调示例一样此处较早的Ajax代码示例也遇到了所有相同的“控制反转”问题(也称为“回调地狱”)。 到目前为止,我们缺少一些观察结果:

  1. There's no clear path for error handling. As we learned in the previous post, we could have detected an error with the Ajax call (somehow), passed it back to our generator with it.throw(..), and then used try..catch in our generator logic to handle it. But that's just more manual work to wire up in the "back-end" (the code handling our generator iterator), and it may not be code we can re-use if we're doing lots of generators in our program.

    没有明确的错误处理路径。 正如我们在上一篇文章中了解到的 ,我们可以检测到Ajax调用发生错误(以某种方式),并使用it.throw(..)将其传递回我们的生成器,然后在生成器逻辑中使用try..catch来处理它。 但这只是连接“后端”(处理生成器迭代器的代码)的更多手动工作,并且如果我们在程序中使用大量生成器,则可能不是可以重复使用的代码。

  2. If the makeAjaxCall(..) utility isn't under our control, and it happens to call the callback multiple times, or signal both success and error simultaneously, etc, then our generator will go haywire (uncaught errors, unexpected values, etc). Handling and preventing such issues is lots of repetitive manual work, also possibly not portable.

    如果makeAjaxCall(..)实用程序不在我们的控制之下,并且碰巧多次调用该回调,或者同时发信号通知成功和错误,等等,那么我们的生成器将陷入困境(未捕获的错误,意外值等) 。 处理和预防此类问题需要大量重复的手动工作,而且可能也无法移植。

  3. Often times we need to do more than one task "in parallel" (like two simultaneous Ajax calls, for instance). Since generator yield statements are each a single pause point, two or more cannot run at the same time -- they have to run one-at-a-time, in order. So, it's not very clear how to fire off multiple tasks at a single generator yield point, without wiring up lots of manual code under the covers.

    通常,我们需要“并行”执行多个任务 (例如,同时执行两个Ajax调用)。 由于生成器yield语句每个都是单个暂停点,因此两个或多个不能同时运行-它们必须一次运行一次。 因此,不清楚如何在单个生成器yield点上启动多个任务,而无需在幕后编写大量手动代码。

As you can see, all of these problems are solvable, but who really wants to reinvent these solutions every time. We need a more powerful pattern that's designed specifically as a trustable, reusable solution for our generator-based async coding.

如您所见,所有这些问题都是可以解决的 ,但是谁真的想每次都重新发明这些解决方案。 我们需要一个更强大的模式,该模式专门设计为基于生成器的异步编码的可信赖,可重用的解决方案

That pattern? yielding out promises, and letting them resume the generator when they fulfill.

那个图案? yield承诺 ,让它们在履行时恢复生成器。

Recall above that we did yield request(..), and that the request(..) utility didn't have any return value, so it was effectively just yield undefined?

回想一下,我们确实yield request(..) ,而request(..)实用程序没有任何返回值,所以它实际上只是yield undefined

Let's adjust that a little bit. Let's change our request(..) utility to be promises-based, so that it returns a promise, and thus what we yield out is actually a promise (and not undefined).

让我们稍微调整一下。 让我们将request(..)实用程序更改为基于Promise的,以便它返回Promise,因此,我们yield 的实际上是Promise (而不是undefined )。

function request(url) {
    // Note: returning a promise now!
    return new Promise( function(resolve,reject){
        makeAjaxCall( url, resolve );
    } );
}


request(..) now constructs a promise that will be resolved when the Ajax call finishes, and we return that promise, so that it can be yielded out. What next?

request(..)现在构造了一个Promise,当Ajax调用完成时,它将被解决,并且我们返回该Promise,以便可以将其yield 。 接下来是什么?

We'll need a utility that controls our generator's iterator, that will receive those yielded promises and wire them up to resume the generator (via next(..)). I'll call this utility runGenerator(..) for now:

我们将需要一个控制生成器迭代器的实用程序,该实用程序将接收那些yield ed promises并将它们连接起来以恢复生成器(通过next(..) )。 我现在将此工具runGenerator(..)

// run (async) a generator to completion
// Note: simplified approach: no error handling here
function runGenerator(g) {
    var it = g(), ret;

    // asynchronously iterate over generator
    (function iterate(val){
        ret = it.next( val );

        if (!ret.done) {
            // poor man's "is it a promise?" test
            if ("then" in ret.value) {
                // wait on the promise
                ret.value.then( iterate );
            }
            // immediate value: just send right back in
            else {
                // avoid synchronous recursion
                setTimeout( function(){
                    iterate( ret.value );
                }, 0 );
            }
        }
    })();
}


Key things to notice:

注意的关键事项:

  1. We automatically initialize the generator (creating its it iterator), and we asynchronously will run it to completion (done:true).

    我们自动初始化发生器(创建其it的迭代器),和我们异步运行it来完成( done:true )。

  2. We look for a promise to be yielded out (aka the return value from each it.next(..) call). If so, we wait for it to complete by registering then(..) on the promise.

    我们寻找可以yield的承诺(也就是每次it.next(..)调用的返回value )。 如果是这样,我们通过在promise上注册then(..)来等待它完成。

  3. If any immediate (aka non-promise) value is returned out, we simply send that value back into the generator so it keeps going immediately.

    如果有任何即时(又是非承诺)值退回,我们只需将该值发送回生成器中,这样它就可以立即运行。

Now, how do we use it?

现在,我们如何使用它?

runGenerator( function *main(){
    var result1 = yield request( "http://some.url.1" );
    var data = JSON.parse( result1 );

    var result2 = yield request( "http://some.url.2?id=" + data.id );
    var resp = JSON.parse( result2 );
    console.log( "The value you asked for: " + resp.value );
} );


Bam! Wait... that's the exact same generator code as earlier? Yep. Again, this is the power of generators being shown off. The fact that we're now creating promises, yielding them out, and resuming the generator on their completion -- ALL OF THAT IS "HIDDEN" IMPLEMENTATION DETAIL! It's not really hidden, it's just separated from the consumption code (our flow control in our generator).

am! 等等...那是与之前完全相同的生成器代码 ? 是的 再次,这是发电机的力量被炫耀。 事实上,我们现在创造的承诺, yield荷兰国际集团出来,并恢复发电机在他们完成- 所有这一切都“隐藏”的实现细节! 它并没有真正隐藏,只是与消耗代码(我们生成器中的流控制)分开了。

By waiting on the yielded out promise, and then sending its completion value back into it.next(..), the result1 = yield request(..) gets the value exactly as it did before.

通过等待yield out的promise,然后将其完成值发送回给它it.next(..)result1 = yield request(..)会像以前一样获得该值。

But now that we're using promises for managing the async part of the generator's code, we solve all the inversion/trust issues from callback-only coding approaches. We get all these solutions to our above issues for "free" by using generators + promises:

但是,既然我们正在使用promise来管理生成器代码的异步部分,我们就可以通过仅回调的编码方法解决所有的反转/信任问题。 通过使用generators + promises,我们可以免费获得针对上述问题的所有这些解决方案:

  1. We now have built-in error handling which is easy to wire up. We didn't show it above in our runGenerator(..), but it's not hard at all to listen for errors from a promise, and wire them to it.throw(..) -- then we can use try..catch in our generator code to catch and handle errors.

    现在,我们具有内置的错误处理功能,可以轻松进行连接。 我们上面没有在runGenerator(..)显示它,但是侦听来自promise的错误并将其连接到它上并不困难it.throw(..) -然后我们可以使用try..catch在我们的生成器代码中捕获和处理错误。

  2. We get all the control/trustability that promises offer. No worries, no fuss.

    我们得到了承诺所提供的所有控制/信任 。 不用担心,不必大惊小怪。

  3. Promises have lots of powerful abstractions on top of them that automatically handle the complexities of multiple "parallel" tasks, etc.

    承诺之上有许多强大的抽象,可以自动处理多个“并行”任务等的复杂性。

    For example, yield Promise.all([ .. ]) would take an array of promises for "parallel" tasks, and yield out a single promise (for the generator to handle), which waits on all of the sub-promises to complete (in whichever order) before proceeding. What you'd get back from the yield expression (when the promise finishes) is an array of all the sub-promise responses, in order of how they were requested (so it's predictable regardless of completion order).

    例如, yield Promise.all([ .. ])将为“并行”任务获取一个承诺数组,并yield一个承诺(供生成器处理),它等待所有子承诺完成(以任何顺序),然​​后再继续。 从yield表达式中得到的回报(当诺言完成时)是所有子承诺响应的数组,按请求的顺序排列(因此,无论完成顺序如何,都是可预测的)。

First, let's explore error handling:

首先,让我们探讨错误处理:

// assume: `makeAjaxCall(..)` now expects an "error-first style" callback (omitted for brevity)
// assume: `runGenerator(..)` now also handles error handling (omitted for brevity)

function request(url) {
    return new Promise( function(resolve,reject){
        // pass an error-first style callback
        makeAjaxCall( url, function(err,text){
            if (err) reject( err );
            else resolve( text );
        } );
    } );
}

runGenerator( function *main(){
    try {
        var result1 = yield request( "http://some.url.1" );
    }
    catch (err) {
        console.log( "Error: " + err );
        return;
    }
    var data = JSON.parse( result1 );

    try {
        var result2 = yield request( "http://some.url.2?id=" + data.id );
    } catch (err) {
        console.log( "Error: " + err );
        return;
    }
    var resp = JSON.parse( result2 );
    console.log( "The value you asked for: " + resp.value );
} );


If a promise rejection (or any other kind of error/exception) happens while the URL fetching is happening, the promise rejection will be mapped to a generator error (using the -- not shown -- it.throw(..) in runGenerator(..)), which will be caught by the try..catch statements.

如果一个承诺拒收(或任何其他类型的错误/异常)的发生,而URL抓取正在发生的事情,承诺拒绝将被映射到一个生成错误(使用-未显示- it.throw(..)runGenerator(..) ),将被try..catch语句捕获。

Now, let's see a more complex example that uses promises for managing even more async complexity:

现在,让我们看一个更复杂的示例,它使用promise管理更多异步复杂性:

function request(url) {
    return new Promise( function(resolve,reject){
        makeAjaxCall( url, resolve );
    } )
    // do some post-processing on the returned text
    .then( function(text){
        // did we just get a (redirect) URL back?
        if (/^https?:\/\/.+/.test( text )) {
            // make another sub-request to the new URL
            return request( text );
        }
        // otherwise, assume text is what we expected to get back
        else {
            return text;
        }
    } );
}

runGenerator( function *main(){
    var search_terms = yield Promise.all( [
        request( "http://some.url.1" ),
        request( "http://some.url.2" ),
        request( "http://some.url.3" )
    ] );

    var search_results = yield request(
        "http://some.url.4?search=" + search_terms.join( "+" )
    );
    var resp = JSON.parse( search_results );

    console.log( "Search results: " + resp.value );
} );


Promise.all([ .. ]) constructs a promise that's waiting on the three sub-promises, and it's that main promise that's yielded out for the runGenerator(..) utility to listen to for generator resumption. The sub-promises can receive a response that looks like another URL to redirect to, and chain off another sub-request promise to the new location. To learn more about promise chaining, read this article section.

Promise.all([ .. ])构造了一个等待三个子承诺的runGenerator(..) ,正是那个主要的runGenerator(..) yield了,让runGenerator(..)实用程序监听生成器的恢复。 子承诺可以接收到一个看起来像要重定向到另一个URL的响应,并将另一个子请求承诺链接到新位置。 要了解有关诺言链的更多信息,请阅读本文部分

Any kind of capability/complexity that promises can handle with asynchronicity, you can gain the sync-looking code benefits by using generators that yield out promises (of promises of promises of ...). It's the best of both worlds.

许诺可以异步处理的任何类型的功能/复杂性,您都可以通过使用产生承诺(...的承诺)的生成器来yield看似同步的代码好处。 这是两全其美。

runGenerator(..) :库实用程序 (runGenerator(..): Library Utility)

We had to define our own runGenerator(..) utility above to enable and smooth out this generator+promise awesomeness. We even omitted (for brevity sake) the full implementation of such a utility, as there's more nuance details related to error-handling to deal with.

我们必须在上面定义我们自己的runGenerator(..)实用程序,以启用和消除此Generator + runGenerator(..) 。 为了简洁起见,我们甚至省略了这种实用程序的完整实现,因为有更多与错误处理有关的细微差别需要处理。

But, you don't want to write your own runGenerator(..) do you?

但是,您不想编写自己的runGenerator(..)吗?

I didn't think so.

我不这么认为。

A variety of promise/async libs provide just such a utility. I won't cover them here, but you can take a look at Q.spawn(..), the co(..) lib, etc.

各种各样的promise / async库提供了这样的实用程序。 我不会在这里介绍它们,但是您可以看一下Q.spawn(..)co(..) lib等。

I will however briefly cover my own library's utility: asynquence's runner(..) plugin, as I think it offers some unique capabilities over the others out there. I wrote an in-depth 2-part blog post series on asynquence if you're interested in learning more than the brief exploration here.

但是,我将简要介绍一下我自己的库的实用程序: asynquence的Runner runner(..)插件 ,因为我认为它提供了一些优于其他库的独特功能。 如果您除了学习简短的探索之外还感兴趣,我就撰写了有关异步的两部分博客文章

First off, asynquence provides utilities for automatically handling the "error-first style" callbacks from the above snippets:

首先, 异步提供了实用程序,用于自动处理上述摘要中的“错误优先样式”回调:

function request(url) {
    return ASQ( function(done){
        // pass an error-first style callback
        makeAjaxCall( url, done.errfcb );
    } );
}


That's much nicer, isn't it!?

好多了 ,不是!?

Next, asynquence's runner(..) plugin consumes a generator right in the middle of an asynquence sequence (asynchronous series of steps), so you can pass message(s) in from the preceding step, and your generator can pass message(s) out, onto the next step, and all errors automatically propagate as you'd expect:

接着,asynquencerunner(..)的插件消耗发电机右在asynquence序列的中间(异步一系列步骤),这样就可以在传递消息(多个)从前面的步骤,和你的发电机可以传递消息( s),进入下一步,所有错误都会按照您的期望自动传播:

// first call `getSomeValues()` which produces a sequence/promise,
// then chain off that sequence for more async steps
getSomeValues()

// now use a generator to process the retrieved values
.runner( function*(token){
    // token.messages will be prefilled with any messages
    // from the previous step
    var value1 = token.messages[0];
    var value2 = token.messages[1];
    var value3 = token.messages[2];

    // make all 3 Ajax requests in parallel, wait for
    // all of them to finish (in whatever order)
    // Note: `ASQ().all(..)` is like `Promise.all(..)`
    var msgs = yield ASQ().all(
        request( "http://some.url.1?v=" + value1 ),
        request( "http://some.url.2?v=" + value2 ),
        request( "http://some.url.3?v=" + value3 )
    );

    // send this message onto the next step
    yield (msgs[0] + msgs[1] + msgs[2]);
} )

// now, send the final result of previous generator
// off to another request
.seq( function(msg){
    return request( "http://some.url.4?msg=" + msg );
} )

// now we're finally all done!
.val( function(result){
    console.log( result ); // success, all done!
} )

// or, we had some error!
.or( function(err) {
    console.log( "Error: " + err );
} );


The asynquence runner(..) utility receives (optional) messages to start the generator, which come from the previous step of the sequence, and are accessible in the generator in the token.messages array.

异步运行器 runner(..)实用程序接收(可选)消息来启动生成器,该消息来自序列的上一步,并且可以在生成器中的token.messages数组中进行访问。

Then, similar to what we demonstrated above with the runGenerator(..) utility, runner(..) listens for either a yielded promise or yielded asynquence sequence (in this case, an ASQ().all(..) sequence of "parallel" steps), and waits for it to complete before resuming the generator.

然后,类似于我们上面使用runGenerator(..)实用工具演示的runGenerator(..)runner(..)侦听yield ed promise或yield esynquence序列(在这种情况下,为ASQ().all(..)序列(“并行”步骤)),并在恢复生成器之前等待完成。

When the generator finishes, the final value it yields out passes along to the next step in the sequence.

生成器完成后,它yield的最终值将输出到序列中的下一步。

Moreover, if any error happens anywhere in this sequence, even inside the generator, it will bubble out to the single or(..) error handler registered.

而且,如果在此序列中的任何地方(甚至在生成器内部)发生任何错误,它将冒泡到注册的单个or(..)错误处理程序中。

asynquence tries to make mixing and matching promises and generators as dead-simple as it could possibly be. You have the freedom to wire up any generator flows alongside promise-based sequence step flows, as you see fit.

异步试图使承诺和生成器的混合和匹配尽可能地简单。 您可以自由地将任何生成器流与基于承诺的序列步骤流连接起来,如您所愿。

ES7 async (ES7 async)

There is a proposal for the ES7 timeline, which looks fairly likely to be accepted, to create still yet another kind of function: an async function, which is like a generator that's automatically wrapped in a utility like runGenerator(..) (or asynquence's' runner(..)). That way, you can send out promises and the async function automatically wires them up to resume itself on completion (no need even for messing around with iterators!).

对于ES7时间轴,有一个提议似乎很容易被接受,它可以创建另一种功能: async function ,它类似于自动包装在runGenerator(..)这样的实用程序中的生成器(或异步) ' runner(..) )。 这样,您可以发送promise,并且async function自动将它们连接起来,以在完成时恢复自身(甚至不需要弄乱迭代器!)。

It will probably look something like this:

它可能看起来像这样:

async function main() {
    var result1 = await request( "http://some.url.1" );
    var data = JSON.parse( result1 );

    var result2 = await request( "http://some.url.2?id=" + data.id );
    var resp = JSON.parse( result2 );
    console.log( "The value you asked for: " + resp.value );
}

main();


As you can see, an async function can be called directly (like main()), with no need for a wrapper utility like runGenerator(..) or ASQ().runner(..) to wrap it. Inside, instead of using yield, you'll use await (another new keyword) that tells the async function to wait for the promise to complete before proceeding.

如您所见,可以直接调用async function (如main() ),而无需像runGenerator(..)ASQ().runner(..)类的包装器实用程序runGenerator(..)进行包装。 在内部,而不是使用yield ,而是使用await (另一个新关键字),它告诉async function在继续进行之前等待promise完成。

Basically, we'll have most of the capability of library-wrapped generators, but directly supported by native syntax.

基本上,我们将具有库包装生成器的大多数功能,但直接由本机语法支持。

Cool, huh!?

酷吧!?

In the meantime, libraries like asynquence give us these runner utilities to make it pretty darn easy to get the most out of our asynchronous generators!

同时,诸如异步之类的为我们提供了这些运行程序实用程序,使充分利用异步生成器变得相当容易!

摘要 (Summary)

Put simply: a generator + yielded promise(s) combines the best of both worlds to get really powerful and elegant sync(-looking) async flow control expression capabilities. With simple wrapper utilities (which many libraries are already providing), we can automatically run our generators to completion, including sane and sync(-looking) error handling!

简而言之:生成器+ yield ed promise(s)结合了两个方面的优点,以获得真正强大而优雅的同步(看起来)异步流控制表达能力。 使用简单的包装器实用程序(许多库已经提供了),我们可以自动运行生成器以完成操作,包括理智和同步(看起来)错误处理!

And in ES7+ land, we'll probably see async functions that let us do that stuff even without a library utility (at least for the base cases)!

在ES7 +领域中,我们可能会看到async function ,即使没有库实用程序,它也可以让我们做这些事情(至少对于基本情况而言)!

The future of async in JavaScript is bright, and only getting brighter! I gotta wear shades.

JavaScript中异步的未来是光明的 ,只会越来越光明! 我得戴上阴影。

But it doesn't end here. There's one last horizon we want to explore:

但这并没有到此结束。 我们要探索的最后一个视野是:

What if you could tie 2 or more generators together, let them run independently but "in parallel", and let them send messages back and forth as they proceed? That would be some super powerful capability, right!?! This pattern is called "CSP" (communicating sequential processes). We'll explore and unlock the power of CSP in the next article. Keep an eye out!

如果您可以将2个或多个生成器捆绑在一起,让它们独立运行但“并行”运行,并让它们在进行过程中来回发送消息,该怎么办? 那将是一些超级强大的功能,对吧! 这种模式称为“ CSP”(通信顺序过程)。 在下一篇文章中,我们将探索并释放CSP的功能。 请注意!

翻译自: https://davidwalsh.name/async-generators

es6 宏观异步和微观异步

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值