异步height:calc
This is a multi-part blog post series highlighting the capabilities of asynquence, a promises-based flow-control abstraction utility.
异步是应许 (asynquence Is Promises)
As we saw in part 1, asynquence is a wrapper abstraction on top of promises, as sequences. A single-step sequence approximates a promise, though they're not identically compatible. However, that's no big deal, since asynquence can both consume and vend standard promises/thenables easily.
正如我们在第1部分中所看到的, 异步是在promise之上作为序列的包装抽象。 尽管它们不完全兼容,但是单步序列近似于一个承诺。 但是,这没什么大不了的,因为异步可以很容易地消耗和出售标准的promise / thenables 。
So, what's the big deal? "I don't need promise abstractions, because their limitations don't bother me." Or: "I already have a promise abstraction/extension lib that I like, that's really popular!"
那么,有什么大不了的? “我不需要承诺抽象,因为它们的局限性不会打扰我。” 或者:“我已经有了一个我喜欢的Promise抽象/扩展库 ,它真的很受欢迎!”
In a sense, I agree with such a sentiment. If you don't see yet the need for asynquence, or if its flavor isn't appealing to you, I can understand not feeling compelled to switch to it.
从某种意义上说,我同意这种观点。 如果您还没有看到异步的需要 ,或者它的风格对您没有吸引力,我可以理解,您不必强迫使用它。
But we have only just scratched the surface of asynquence. If you just stop here, you've missed the much bigger picture. Please, read on.
但是,我们只是刮擦了异步的表面。 如果您只是在这里停留,那么您将错过更大的前景。 请继续阅读。
异步还更多...并且还在增长! (asynquence Is Much More... And Growing!)
Firstly, we should talk about asynquence can be extended to do more than it ships with. I think this is one of the most interesting parts of the utility, especially given how small the package is, and how few of its peers (even much bigger ones) give this level of capability.
首先,我们应该谈论异步可以扩展到做更多的事情。 我认为这是该实用程序中最有趣的部分之一,尤其是考虑到程序包多么小,以及有多少同行(甚至是更大的同行)提供这种级别的功能,尤其如此。
The entire list of asynquence-contrib plugins are provided as optional extensions to the core asynquence capability. That means they're a great place to start to inspect how you might make your own extensions.
异步约束插件的完整列表作为核心异步功能的可选扩展提供。 这意味着它们是开始检查您如何进行自己的扩展的好地方。
A couple of them just add extra static helpers to the ASQ
namespace, such as ASQ.iterable(..)
(which we'll get to later). But most of them add chainable methods to the instance API, so that you can do things like call the first(..)
plugin in mid-sequence chain, like ASQ().then(..).first(..).then(..)..
. That's pretty powerful.
其中几个只是向ASQ
名称空间添加了额外的静态助手,例如ASQ.iterable(..)
(我们将在后面介绍)。 但是它们大多数都将可链接方法添加到实例API,以便您可以执行诸如调用中间序列链中的first(..)
插件之类的操作,例如ASQ().then(..).first(..).then(..)..
那非常强大。
Let's imagine a simple scenario: You find yourself regularly wanting to log (to the dev console, for instance) the value of some message as it passes through a certain step of your sequence. Here's how you normally do it:
让我们想象一个简单的场景:您发现自己经常想要在消息通过序列中的某个步骤时记录(例如,到开发控制台)消息的值。 通常是这样的:
ASQ(..)
.then(..)
.val(function(msg){
console.log(msg);
return msg;
})
.then(..)
..
Would it be nice to have a re-usable way of doing that? You could declare one, like:
拥有一种可重复使用的方式会很好吗? 您可以声明一个,例如:
function ASQlog(msg) {
console.log(msg);
return msg;
}
ASQ(..)
.then(..)
.val( ASQlog )
.then(..)
..
But we can make it even better, with our own custom contrib plugin. First, here's how we use it:
但是我们可以使用我们自己的自定义contrib插件使其变得更好。 首先,这是我们的用法:
ASQ(..)
.then(..)
.log()
.then(..)
..
Ooo, that's nicer! How do we do it? Make a file called "plugin.log.js" in the contrib package root, then put something like this in it:
噢,那更好! 我们该怎么做呢? 在contrib包的根目录中创建一个名为“ plugin.log.js”的文件,然后在其中添加以下内容:
ASQ.extend( "log", function __log__(api,internals){
return function __log__() {
api.val(function(msg){
console.log(msg);
return msg;
});
return api;
};
});
That's easy, right!? Basically, whatever normal usage you find of the public ASQ API that you repeat frequently, you can wrap up that same sort of call
那很容易,对!! 基本上,无论您发现经常重复使用的公共ASQ API的正常用法如何,都可以包装相同的调用
Now, let's make it a little more robust (to handle more than one success message passing through) and also make it log out any errors:
现在,让我们把它一点点更强劲(以处理多个成功的消息通过传递), 也使其退出任何错误:
ASQ.extend( "log", function __log__(api,internals){
return function __log__() {
api.val(function(){
console.log.apply(console,arguments);
return ASQ.messages.apply(null,arguments);
})
.or(function(){
console.error.apply(console,arguments);
});
return api;
};
});
Here you see the use of the ASQ.messages(..)
utility. That's a simple way of creating an array of values that is specifically branded by ASQ
so that the array can be recognized and unwrapped (into positional parameters) where appropriate.
在这里,您可以看到ASQ.messages(..)
实用程序的ASQ.messages(..)
。 这是创建由ASQ
专门标记的值数组的简单方法,以便可以在适当的地方识别并展开该数组(放入位置参数中)。
Let's make another silly example:
让我们再举一个愚蠢的例子:
ASQ("foo and bar are awesome!")
.fOObAR()
.log(); // "fOO and bAR are awesome!"
How?
怎么样?
ASQ.extend( "fOObAR", function __fOObAR__(api,internals){
return function __fOObAR__() {
api.val(function(msg){
return msg
.replace(/\bfoo\b/g,"fOO")
.replace(/\bbar\b/g,"bAR");
});
return api;
};
});
可重复序列 (Iterable Sequences)
If you look at how sequences work, they internally advanced themselves by calling the each step's respective trigger (just like promises do). But there are certainly cases where being able to advance a sequence from the outside would be nice.
如果您查看序列的工作方式,它们会通过调用每个步骤各自的触发器在内部进行自我提升(就像promise一样)。 但是肯定在某些情况下能够从外部进行序列比较好。
For example, let's imagine a one-time-only event like DOMContentLoaded
, where you need to advanced a main sequence only when that event occurs.
例如,让我们想象一个一次性事件,例如DOMContentLoaded
,您仅在该事件发生时才需要推进主序列。
Here's how you have to "hack" it if all you have is asynquence core:
如果您只有异步核心,请按照以下步骤“破解”它:
ASQ(function(done){
document.addEventListener("DOMContentLoaded",done,false);
})
.then(..)
..
Or, you do "capability extraction" (unfortunately more common in Promises than I think it should be), to get better separation of concerns/capabilities:
或者,您进行“能力提取”(不幸的是,在Promises中比我想的要普遍得多),以便更好地分离关注点/能力:
var trigger;
ASQ(function(done){
trigger = done; // extract the trigger
})
.then(..)
..
// later, elsewhere
document.addEventListener("DOMContentLoaded",trigger,false);
All of those options and their variations suck, especially when you consider a multi-step initialization before the main sequence fires, like both the DOMContentLoaded
firing and an initial setup Ajax request coming back.
所有这些选项及其变体都很DOMContentLoaded
,尤其是当您考虑在主序列触发之前进行多步初始化时,例如DOMContentLoaded
触发和返回初始设置Ajax请求。
So, we now introduce a somewhat different concept, provided by the iterable(..)
plugin: iterable-sequences. These are sequences which are not internally advanceable, but are instead advanced externally, with the familiar Iterator interface: .next(..)
.
因此,我们现在介绍一个由iterable(..)
插件提供的稍微不同的概念: iterable-sequences 。 这些序列不可在内部进行扩展,而应使用熟悉的Iterator接口: .next(..)
外部进行扩展。
Each step of the iterable-sequence doesn't get its own trigger, and there are also no automatically passed success messages from step to step. Instead, you pass a message in with next(..)
, and you get a value back out at the end of the step (an operation that is itself fundamentally synchronous). The "async" nature of these sequences is external to the sequence, hidden away in whatever logic controls the sequence's iteration.
可迭代序列的每个步骤都没有自己的触发器,也没有一步一步自动传递成功消息。 而是使用next(..)
传递消息,并在步骤结束时返回一个值(该操作本质上是同步的)。 这些序列的“异步”性质在序列外部,隐藏在控制序列迭代的任何逻辑中。
DOMContentLoaded
example:
DOMContentLoaded
示例:
var trigger = ASQ.iterable();
document.addEventListener("DOMContentLoaded",trigger.next,false);
// setup main async flow-control
ASQ( trigger ) // wait for trigger to fire before proceeding
.then(..)
.then(..)
..
Or for multi-step:
或进行多步操作:
var noop = function(){};
var setup = ASQ.iterable().then(noop);
document.addEventListener("DOMContentLoaded",setup.next,false);
ajax("some-url",function(response){
// do stuff with response
setup.next();
});
// setup main async flow-control
ASQ( setup ) // wait for setup to complete before proceeding
.then(..)
.then(..)
..
迭代可迭代序列 (Iterating Iterable-Sequences)
Iterable-sequences can also be set up to have a pre-defined (or even infinite) set of steps, and then it can be iterated on using normal iteration techniques.
还可以将可迭代序列设置为具有一组预定义(甚至是无限个)的步骤,然后可以使用常规迭代技术对其进行迭代。
For example, to manually sync iterate an iterable-sequence with a for
loop:
例如,要手动同步带有for
循环的可迭代序列:
function double(x) { return x * 2; }
function triple(x) { return x * 3; }
var isq = ASQ.iterable()
.then(double)
.then(double)
.then(triple);
for (var seed = 3, ret;
(ret = isq.next(seed)) && !ret.done;
) {
seed = ret.value;
console.log(seed);
}
// 6
// 12
// 36
Even better, ES6 gives us @@Iterator
hooks, plus the for..of
loop, to automatically iterate over iterable-sequences (assuming each step doesn't need input):
更好的是,ES6为我们提供了@@Iterator
挂钩,以及for..of
循环,以自动迭代可迭代序列(假设每个步骤都不需要输入):
var x = 0;
function inc() { return ++x; }
var isq = ASQ.iterable()
.then(inc)
.then(inc)
.then(inc);
for (var v of isq) {
console.log(v);
}
// 1
// 2
// 3
Of course, these are examples of iterating an iterable-sequence synchronously, but it's trivial to imagine how you call next(..)
inside of async tasks like timers, event handlers, etc, which has the effect of asynchronously stepping through the iterable-sequence's steps.
当然,这些是同步迭代可迭代序列的示例,但是想像一下如何在计时器,事件处理程序等异步任务中调用next(..)
并不容易,这具有异步步进可迭代序列的作用。序列的步骤。
In this way, iterable-sequences are kind of like generators (which we'll cover next), where each step is like a yield
, and next(..)
restarts the sequence/generator.
这样,可迭代序列有点像生成器(我们将在后面介绍),其中每个步骤就像yield
,而next(..)
重新启动序列/生成器。
发电机 (Generators)
In addition to Promise
, ES6 adds generators capability, which is another huge addition to JS's ability to handle async programming more sanely.
除了Promise
,ES6还增加了generators功能,这是JS 更合理地处理异步编程功能的又一个巨大补充。
I won't teach all of generators here (there's plenty of stuff already written about them). But let me just quickly code the previous example with a generator instead, for illustration purposes:
我不会在这里教所有的生成器(已经有很多关于它们的文章)。 但是,为了说明起见,让我快速地使用生成器来编码前面的示例:
function* gen() {
var x = 0;
yield ++x;
yield ++x;
yield ++x;
}
for ( var v of gen() ) {
console.log(v);
}
// 1
// 2
// 3
As you can see, generators essentially look like synchronous code, but the yield
keyword pauses it mid-execution, optionally returning a value. The for..of
loop hides the next()
calls, and thus sends nothing in, but you could manually iterate a generator if you needed to pass values in at each iteration, just like I did above with iterable-sequences.
如您所见,生成器本质上看起来像同步代码,但是yield
关键字在执行过程中将其暂停,可以选择返回一个值。 for..of
循环隐藏了next()
调用,因此不发送任何内容,但是如果您需要在每次迭代中传递值,则可以手动迭代生成器,就像我上面对可迭代序列所做的那样。
But this isn't the cool part of generators. The cool part is when generators are combined with promises. For example:
但这不是发电机的酷部分。 最酷的部分是当发电机与承诺结合在一起时。 例如:
function asyncIncrement(x) {
return new Promise(function(resolve){
setTimeout(function(){
resolve(++x);
},500);
});
}
runAsyncGenerator(function*(){
var x = 0;
while (x < 3) {
x = yield asyncIncrement(x);
}
console.log(x);
});
// 3
Some very important things to notice:
需要注意的一些非常重要的事情:
I have used some mythical
runAsyncGenerator(..)
utility. We'll come back to that in a minute.我使用了一些神话般的
runAsyncGenerator(..)
实用程序。 我们待会儿再讲。What we
yield
out of our generator is actually a promise for a value, rather than an immediate value. We obviously get something back after our promise completes, and that something is the incremented number.我们从生成器中产生的
yield
实际上是对价值的承诺,而不是即时价值。 显然,在诺言完成之后,我们得到了一些回报,那就是递增的数字。
Inside the runAsyncGenerator(..)
utility, I would have an iterator controlling my generator, which would be calling next(..)
on it successively.
在runAsyncGenerator(..)
实用程序内部,我将有一个迭代器来控制我的生成器,该生成器将依次对其调用next(..)
。
What it gets back from a next(..)
call is a promise, so we just listen for that promise to finish, and when it does, we take its success value and pass it back into the next next(..)
call.
它从next(..)
调用返回的内容是promise ,因此我们只听该诺言完成,而当它完成时,我们将其成功值传递给下next(..)
调用。
In other words, runAsyncGenerator(..)
automatically and asynchronously runs our generator to its completion, with each async promise "step" just pausing the iteration until resolution.
换句话说, runAsyncGenerator(..)
自动并异步地运行我们的生成器使其完成,每个异步承诺“步骤”都只是暂停迭代直到解决。
This is a hugely powerful technique, as it allows us to write sync-looking code, like our while
loop, but hide away as an implementation detail the fact that the promises we yield
out introduce asynchronicity into the iteration loop.
这是一个非常强大的技术,因为它允许我们编写同步看代码,就像我们while
循环,但遁形作为一个实现细节的事实是,我们的承诺yield
了引入异步到迭代循环。
异步性 ? (asynquence?)
Several other async/promises libraries have a utility like runAsyncGenerator(..)
already built-in (called spawn(..)
or co(..)
, etc). And so does asynquence, called runner(..)
. But the one asynquence provides is much more powerful!
其他几个异步/承诺库具有已内置的实用程序,如runAsyncGenerator(..)
(称为spawn(..)
或co(..)
等)。 称为Runner runner(..)
异步也是如此。 但是, 异步提供的功能要强大得多!
The most important thing is that asynquence lets you wire a generator up to run right in the middle of a normal sequence, like a specialized then(..)
sort of step, which also lets you pass previous sequence step messages into the generator, and it lets you yield
value(s) out from the end of the generator to continue in the main sequence.
最重要的是,asynquence可以让你连接一个发电机达权在正常序列的中间跑,像一个专业then(..)
排序步骤,这也可以让你通过前面的顺序步骤的消息到发电机,并它使您可以从生成器的末尾yield
值,以继续执行主序列。
To my knowledge, no other library has that capability! Let's see what it looks like:
据我所知,没有其他图书馆具有这种能力! 让我们看看它是什么样的:
function inc(x,y) {
return ASQ(function(done){
setTimeout(function(){
done(x + y);
},500);
});
}
ASQ( 3, 4 )
.runner(function*(control){
var x = control.messages[0];
var y = control.messages[1];
while (x < 20) {
x = yield inc(x,y);
}
// Note: `23` was the last value yielded out,
// so it's automatically the success value from
// the generator. If you wanted to send some
// other value out, just call another `yield __`
// here.
})
.val(function(msg){
console.log(msg); // 23
});
The inc(..)
shown returns an asynquence instance, but it would have worked identically if it had returned a normal promise, as runner(..)
listens for either promises or sequences and treats them appropriately. Of course, you could have yielded out a much more complex, multi-step sequence (or promise-chain) if you wanted, and runner(..)
would just sit around patiently waiting.
所示的inc(..)
返回一个异步实例,但是如果它返回了一个正常的promise,它会以相同的方式工作,因为Runner runner(..)
侦听promise或序列并对其进行适当的对待。 当然,如果您愿意的话,您可能会得出更复杂的多步骤序列(或Promise链),并且runner(..)
只会耐心等待。
That's pretty powerful, don't you think!? Generators + Promises unquestionablly represents the future direction of async programming in JS. In fact, early proposals for ES7 suggest we'll get async
functions which will have native syntactic support for what spawn(..)
and runner(..)
do. Super exciting!
那非常强大,您不认为!! Generators + Promises无疑代表了JS中异步编程的未来方向。 实际上,早期针对ES7的建议建议我们将获得async
函数,该函数将对spawn(..)
和runner(..)
行为提供本地语法支持。 超级刺激!
But that's just barely scratching the surface of how asynquence leverages the power of generators.
但这仅仅是勉强了解异步如何利用发电机的力量。
CSP风格的并发(如go) (CSP-style Concurrency (like go))
We just saw the power of a single generator being run-to-completion in the middle of a sequence.
我们只是看到单个发电机的功率在序列的中间运行到完成。
But what if you paired two or more generators together, so that they yield back-and-forth to each other? In essence, you'd be accomplishing CSP-style (Communicating Sequential Processes) concurrency, where each generator was like a sequential "process", and they cooperatively interleaved their own individual steps. They also have a shared message channel to send messages between them.
但是,如果将两个或多个生成器配对在一起,使它们彼此来回交互,该怎么办? 在本质上,你会办成CSP式(C ommunicating 小号 equential P rocesses)并发,其中每个发电机像一个连续的“过程”,和他们共同交织自己的各个步骤。 它们还具有共享的消息通道以在它们之间发送消息。
I cannot overstate the power of this pattern.
我不能夸大这种模式的威力。
It's basically what the go language supports naturally, and what ClojureScript's core.async
functionality automatically creates in JS. I highly recommend you read David Nolen's fantastic writings on the topic, like this post and this post, as well as others. Also, check out his Om framework which makes use of these ideas and more.
基本上, go语言是自然支持的语言,也是ClojureScript的core.async
功能在JS中自动创建的功能。 我强烈建议您阅读David Nolen关于该主题的出色著作,例如本博文 , 本博文以及其他。 另外,请查看他的Om框架 ,该框架充分利用了这些想法。
In fact, there's also a stand-alone library for exactly this CSP-style concurrency task, called js-csp.
实际上,还有一个完全独立的库,称为js-csp ,用于此CSP风格的并发任务。
异步 CSP风格 (asynquence CSP-style)
But this post is about asynquence, right? Rather than needing a separate library or different language, the power of asynquence is that you can do CSP-style programming with the same utility that you do all your other promises work.
但是这篇文章是关于异步的 ,对吧? 异步的功能不是需要单独的库或不同的语言, 而是可以使用与执行所有其他Promise相同的实用程序来进行CSP风格的编程。
Rather than fully teaching the whole concept, I'll choose to just illustrate it with code and let you examine and learn to whatever extent this piques your interest. I personally feel this is a big part of the future of advanced async programming in the language.
我不会完全讲授整个概念,而是选择仅用代码说明它,并让您检查和学习,这在多大程度上激发了您的兴趣。 我个人认为,这是该语言高级异步编程未来的重要组成部分。
I'm going to rip/fork/port this example directly from go and js-csp... the classic "Ping Pong" demo example. To see it work, run the demo in a browser (Note: currently, only Chrome's generators are spec-compliant enough to run the example -- FF is close but not quite there).
我将直接从go和js-csp翻录/分叉/移植此示例...经典的“乒乓”演示示例。 要查看其工作原理,请在浏览器中运行该演示( 注意:目前,只有Chrome的生成器符合规范,足以运行该示例-FF已经关闭,但还不足够)。
A snippet of the demo's code:
演示代码片段:
ASQ(
["ping","pong"], // player names
{ hits: 0 } // the ball
)
.runner(
referee,
player,
player
)
.val(function(msg){
console.log("referee",msg); // "Time's up!"
});
Briefly, if you examine the full JS code at that demo link, you can see 3 generators (referee
and two instances of player
) that are run by runner(..)
, trading control with each other (by yield table
statements), and messaging each other through the shared message channels in table.messages
.
简而言之,如果您在该演示链接上检查了完整的JS代码,则可以看到3个生成器( referee
和两个player
实例)由runner(..)
运行,彼此进行控制交易(通过yield table
语句),并且通过table.messages
的共享消息通道彼此进行消息table.messages
。
You can still yield promises/sequences from a generator, as yield sleep(500)
does, which doesn't transfer control but just pauses that generator's progression until the promise/sequence completes.
您仍然可以从生成器生成承诺/序列,就像yield sleep(500)
一样,它不转移控制权,只是暂停生成器的进程,直到承诺/序列完成。
Again... wow. Generators paired together as CSP-style coroutines is a huge and largely untapped horizon we're just starting to advanced towards. asynquence is on the leading edge of that evolution, letting you explore the power of these techniques right alongside the more familiar promises capabilities. No framework switching -- it's all in one utility.
再次...哇。 生成器与CSP样式的协程配对在一起,是一个巨大且尚未开发的视野,我们才刚刚开始前进。 异步是这种发展的前沿,让您可以与更熟悉的promise功能一起探索这些技术的力量。 无需框架切换-全都在一个实用程序中。
事件React (Event-Reactive)
OK, the last advanced pattern I am going to explore here with asynquence is the "reactive observables" pattern from the RxJS -- Reactive Extensions library from the smart folks (like Matt Podwysocki) at Microsoft. I was inspired by their "reactive observables" and added a similar concept, which I call "reactive sequences", via the react(..)
plugin.
好的,我将在此处以异步方式探讨的最后一个高级模式是RxJS的“React式可观察到”模式-来自Microsoft的聪明人(如Matt Podwysocki )的React式扩展库。 他们的“React性可观察物”启发了我,并通过react(..)
插件添加了一个类似的概念,称为“React性序列”。
Briefly, the problem we want to address is that promises only work well for single-fire types of events. What if you had a repeating event (like a button click) that you wanted to fire off a sequence of events for each trigger?
简而言之,我们要解决的问题是,诺言仅适用于单发事件类型。 如果您有一个重复事件(如按钮单击)想要为每个触发器触发一系列事件怎么办?
We could do it like this:
我们可以这样做:
$("#button").click(function(evt){
ASQ(..)
.then(..)
.then(..)
..
});
But that kinda sucks for separation of concerns/capabilities. We'd like to be able to separate the specification of the flow-control sequence from the listening for the event that will fire it off. In other words, we'd like to invert that example's "nesting".
但这有点使关注点/能力的分离变得糟透了。 我们希望能够将流控制序列的规范与侦听将触发它的事件分开。 换句话说,我们想反转该示例的“嵌套”。
The asynquence react(..)
plugin gives you that capability:
异步 react(..)
插件为您提供了以下功能:
var sq = ASQ.react(function(trigger){
$("#button").click(trigger);
});
// elsewhere:
sq
.then(..)
.then(..)
..
Each time the trigger
function is called, a new copy of the defined sequence (aka template) is spun off and runs independently.
每次调用trigger
函数时,都会旋转定义序列的新副本 (又名模板),并独立运行。
Though not shown here, you can also register steps to take when tearing down the reactive-sequence (to unbind handlers, etc). There's also a special helper for listening for events on node.js streams.
尽管此处未显示,但您也可以注册拆卸React式序列时要采取的步骤(解除绑定处理程序等)。 还有一个特殊的帮助程序,用于侦听node.js流上的事件。
Here's some more concrete examples:
这里有一些更具体的例子:
So, bottom line, you could easily switch to using the whole RxJS library (it's quite large/complex but extremely capable!) for such event-reactive async programming, or you can use *asynquence and get some of that important functionality just built-in to the utility that already handles your other async flow-control tasks.
因此,最重要的是,您可以轻松地使用整个RxJS库(它非常大/复杂,但功能强大!)来进行事件响应式异步编程, 或者您可以使用* asynquence并获得一些刚刚构建的重要功能,进入已经处理了其他异步流控制任务的实用程序。
结语 (Wrapping Up)
I think you can probably agree by now: that's a whole bunch of advanced functionality and patterns you get out-of-the-box with asynquence.
我想您现在可能已经同意了:这是一大堆高级功能和模式,您可以立即使用asynquence 。
I encourage you to give asynquence a shot and see if it doesn't simplify and revolutionize your async coding in JS.
我鼓励您尝试一下异步,看看它是否不会简化和革新JS中的异步编码。
And if you find something that's substantially missing in terms of functionality, I bet we can write a plugin that'll do it pretty easily!
而且,如果您发现在功能方面实质上缺少的东西,我敢打赌,我们可以编写一个可以轻松完成此工作的插件!
Here's the most important take-away I can leave you with: I didn't write asynquence or this blog post series just so that you'd use the lib (although I hope you give it a shot). I built it in the open, and wrote these public posts, to inspire you to help me make it better and better.
这是我可以带给您的最重要的收获 :我没有写异步或本博客系列文章, 只是为了让您使用lib(尽管我希望您能尝试一下)。 我在公开场合构建它,并写了这些公开帖子,以激励您帮助我变得越来越好。
I want asynquence to be the most powerful collection of async flow-control utilities anywhere. You can help me make that happen.
我希望异步成为任何地方最强大的异步流控制实用程序集合。 您可以帮助我实现这一目标。
异步height:calc