构造函数可以被重载吗 async吗

【转】C# Async/Await 异步编程中的最佳做法 - 梦琪小生 - 博客园
Async/Await
异步编程中的最佳做法
近日来,涌现了许多关于 Microsoft .NET Framework 4.5 中新增了对 async 和 await 支持的信息。&本文旨在作为学习异步编程的“第二步”;我假设您已阅读过有关这一方面的至少一篇介绍性文章。&本文不提供任何新内容,Stack Overflow、MSDN 论坛和 async/await FAQ 这类在线资源提供了同样的建议。&本文只重点介绍一些淹没在文档海洋中的最佳做法。
本文中的最佳做法更大程度上是“指导原则”,而不是实际规则。&其中每个指导原则都有一些例外情况。&我将解释每个指导原则背后的原因,以便可以清楚地了解何时适用以及何时不适用。&图 1&中总结了这些指导原则;我将在以下各节中逐一讨论。
图 1 异步编程指导原则总结
避免 Async Void
最好使用 async Task 方法而不是 async void 方法
事件处理程序
始终使用 Async
不要混合阻塞式代码和异步代码
控制台 main 方法
配置上下文
尽可能使用 ConfigureAwait(false)
需要上下文的方法
避免 Async Void
Async 方法有三种可能的返回类型: Task、Task&T& 和 void,但是 async 方法的固有返回类型只有 Task 和 Task&T&。&当从同步转换为异步代码时,任何返回类型 T 的方法都会成为返回 Task&T& 的 async 方法,任何返回 void 的方法都会成为返回 Task 的 async 方法。&下面的代码段演示了一个返回 void 的同步方法及其等效的异步方法:
void MyMethod()
// Do synchronous work.
Thread.Sleep(<span style="color: #00);
async Task MyMethodAsync()
// Do asynchronous work.
await Task.Delay(<span style="color: #00);
返回 void 的 async 方法具有特定用途: 用于支持异步事件处理程序。&事件处理程序可以返回某些实际类型,但无法以相关语言正常工作;调用返回类型的事件处理程序非常困难,事件处理程序实际返回某些内容这一概念也没有太大意义。&事件处理程序本质上返回 void,因此 async 方法返回 void,以便可以使用异步事件处理程序。&但是,async void 方法的一些语义与 async Task 或 async Task&T& 方法的语义略有不同。
Async void 方法具有不同的错误处理语义。&当 async Task 或 async Task&T& 方法引发异常时,会捕获该异常并将其置于 Task 对象上。&对于 async void 方法,没有 Task 对象,因此 async void 方法引发的任何异常都会直接在 SynchronizationContext(在 async void 方法启动时处于活动状态)上引发。&图 2&演示本质上无法捕获从 async void 方法引发的异常。
图 2 无法使用 Catch 捕获来自 Async Void 方法的异常
private async void ThrowExceptionAsync()
throw new InvalidOperationException();
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
ThrowExceptionAsync();
catch (Exception)
// The exception is never caught here!
可以通过对 GUI/ASP.NET 应用程序使用 AppDomain.UnhandledException 或类似的全部捕获事件观察到这些异常,但是使用这些事件进行常规异常处理会导致无法维护。
Async void 方法具有不同的组合语义。&返回 Task 或 Task&T& 的 async 方法可以使用 await、Task.WhenAny、Task.WhenAll 等方便地组合而成。&返回 void 的 async 方法未提供一种简单方式,用于向调用代码通知它们已完成。&启动几个 async void 方法不难,但是确定它们何时结束却不易。&Async void 方法会在启动和结束时通知 SynchronizationContext,但是对于常规应用程序代码而言,自定义 SynchronizationContext 是一种复杂的解决方案。
Async void 方法难以测试。&由于错误处理和组合方面的差异,因此调用 async void 方法的单元测试不易编写。&MSTest 异步测试支持仅适用于返回 Task 或 Task&T& 的 async 方法。&可以安装 SynchronizationContext 来检测所有 async void 方法都已完成的时间并收集所有异常,不过只需使 async void 方法改为返回 Task,这会简单得多。
显然,async void 方法与 async Task 方法相比具有几个缺点,但是这些方法在一种特定情况下十分有用: 异步事件处理程序。&语义方面的差异对于异步事件处理程序十分有意义。&它们会直接在 SynchronizationContext 上引发异常,这类似于同步事件处理程序的行为方式。&同步事件处理程序通常是私有的,因此无法组合或直接测试。&我喜欢采用的一个方法是尽量减少异步事件处理程序中的代码(例如,让它等待包含实际逻辑的 async Task 方法)。&下面的代码演示了这一方法,该方法通过将 async void 方法用于事件处理程序而不牺牲可测试性:
private async void button1_Click(object sender, EventArgs e)
await Button1ClickAsync();
public async Task Button1ClickAsync()
// Do asynchronous work.
await Task.Delay(<span style="color: #00);
如果调用方不希望 async void 方法是异步的,则这些方法可能会造成严重影响。&当返回类型是 Task 时,调用方知道它在处理将来的操作;当返回类型是 void 时,调用方可能假设方法在返回时完成。&此问题可能会以许多意外方式出现。&在接口(或基类)上提供返回 void 的方法的 async 实现(或重写)通常是错误的。&某些事件也假设其处理程序在返回时完成。&一个不易察觉的陷阱是将 async lambda 传递到采用 Action 参数的方法;在这种情况下,async lambda 返回 void 并继承 async void 方法的所有问题。&一般而言,仅当 async lambda 转换为返回 Task 的委托类型(例如,Func&Task&)时,才应使用 async lambda。
总结这第一个指导原则便是,应首选 async Task 而不是 async void。&Async Task 方法更便于实现错误处理、可组合性和可测试性。&此指导原则的例外情况是异步事件处理程序,这类处理程序必须返回 void。&此例外情况包括逻辑上是事件处理程序的方法,即使它们字面上不是事件处理程序(例如 ICommand.Execute implementations)。
始终使用 Async
异步代码让我想起了一个故事,有个人提出世界是悬浮在太空中的,但是一个老妇人立即提出质疑,她声称世界位于一个巨大乌龟的背上。&当这个人问乌龟站在哪里时,老夫人回答:“很聪明,年轻人,下面是一连串的乌龟!”在将同步代码转换为异步代码时,您会发现,如果异步代码调用其他异步代码并且被其他异步代码所调用,则效果最好 — 一路向下(或者也可以说“向上”)。&其他人已注意到异步编程的传播行为,并将其称为“传染”或将其与僵尸病毒进行比较。&无论是乌龟还是僵尸,无可置疑的是,异步代码趋向于推动周围的代码也成为异步代码。&此行为是所有类型的异步编程中所固有的,而不仅仅是新 async/await 关键字。
“始终异步”表示,在未慎重考虑后果的情况下,不应混合使用同步和异步代码。&具体而言,通过调用 Task.Wait 或 Task.Result 在异步代码上进行阻塞通常很糟糕。&对于在异步编程方面“浅尝辄止”的程序员,这是个特别常见的问题,他们仅仅转换一小部分应用程序,并采用同步 API 包装它,以便代码更改与应用程序的其余部分隔离。&不幸的是,他们会遇到与死锁有关的问题。&在 MSDN 论坛、Stack Overflow 和电子邮件中回答了许多与异步相关的问题之后,我可以说,迄今为止,这是异步初学者在了解基础知识之后最常提问的问题: “为何我的部分异步代码死锁?”
图 3&演示一个简单示例,其中一个方法发生阻塞,等待 async 方法的结果。&此代码仅在控制台应用程序中工作良好,但是在从 GUI 或 ASP.NET 上下文调用时会死锁。&此行为可能会令人困惑,尤其是通过调试程序单步执行时,这意味着没完没了的等待。&在调用 Task.Wait 时,导致死锁的实际原因在调用堆栈中上移。
图 3 在异步代码上阻塞时的常见死锁问题
public static class DeadlockDemo
private static async Task DelayAsync()
await Task.Delay(<span style="color: #00);
// This method causes a deadlock when called in a GUI or ASP.NET context.
public static void Test()
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
delayTask.Wait();
这种死锁的根本原因是 await 处理上下文的方式。&默认情况下,当等待未完成的 Task 时,会捕获当前“上下文”,在 Task 完成时使用该上下文恢复方法的执行。&此“上下文”是当前 SynchronizationContext(除非它是 null,这种情况下则为当前 TaskScheduler)。&GUI 和 ASP.NET 应用程序具有 SynchronizationContext,它每次仅允许一个代码区块运行。&当 await 完成时,它会尝试在捕获的上下文中执行 async 方法的剩余部分。&但是该上下文已含有一个线程,该线程在(同步)等待 async 方法完成。它们相互等待对方,从而导致死锁。
请注意,控制台应用程序不会形成这种死锁。&它们具有线程池 SynchronizationContext 而不是每次执行一个区块的 SynchronizationContext,因此当 await 完成时,它会在线程池线程上安排 async 方法的剩余部分。&该方法能够完成,并完成其返回任务,因此不存在死锁。&当程序员编写测试控制台程序,观察到部分异步代码按预期方式工作,然后将相同代码移动到 GUI 或 ASP.NET 应用程序中会发生死锁,此行为差异可能会令人困惑。
此问题的最佳解决方案是允许异步代码通过基本代码自然扩展。&如果采用此解决方案,则会看到异步代码扩展到其入口点(通常是事件处理程序或控制器操作)。&控制台应用程序不能完全采用此解决方案,因为 Main 方法不能是 async。&如果 Main 方法是 async,则可能会在完成之前返回,从而导致程序结束。&图 4演示了指导原则的这一例外情况: 控制台应用程序的 Main 方法是代码可以在异步方法上阻塞为数不多的几种情况之一。
图 4 Main 方法可以调用 Task.Wait 或 Task.Result
class Program
static void Main()
MainAsync().Wait();
static async Task MainAsync()
// Asynchronous implementation.
await Task.Delay(<span style="color: #00);
catch (Exception ex)
// Handle exceptions.
允许异步代码通过基本代码扩展是最佳解决方案,但是这意味着需进行许多初始工作,该应用程序才能体现出异步代码的实际好处。&可通过几种方法逐渐将大量基本代码转换为异步代码,但是这超出了本文的范围。在某些情况下,使用 Task.Wait 或 Task.Result 可能有助于进行部分转换,但是需要了解死锁问题以及错误处理问题。&我现在说明错误处理问题,并在本文后面演示如何避免死锁问题。
每个 Task 都会存储一个异常列表。&等待 Task 时,会重新引发第一个异常,因此可以捕获特定异常类型(如 InvalidOperationException)。&但是,在 Task 上使用 Task.Wait 或 Task.Result 同步阻塞时,所有异常都会用 AggregateException 包装后引发。&请再次参阅图 4。&MainAsync 中的 try/catch 会捕获特定异常类型,但是如果将 try/catch 置于 Main 中,则它会始终捕获 AggregateException。&当没有 AggregateException 时,错误处理要容易处理得多,因此我将“全局”try/catch 置于 MainAsync 中。
至此,我演示了两个与异步代码上阻塞有关的问题: 可能的死锁和更复杂的错误处理。&对于在 async 方法中使用阻塞代码,也有一个问题。&请考虑此简单示例:
public static class NotFullyAsynchronousDemo
// This method synchronously blocks a thread.
public static async Task TestNotFullyAsync()
await Task.Yield();
Thread.Sleep(<span style="color: #00);
此方法不是完全异步的。&它会立即放弃,返回未完成的任务,但是当它恢复执行时,会同步阻塞线程正在运行的任何内容。&如果此方法是从 GUI 上下文调用,则它会阻塞 GUI 线程;如果是从 ASP.NET 请求上下文调用,则会阻塞当前 ASP.NET 请求线程。&如果异步代码不同步阻塞,则其工作效果最佳。&图 5&是将同步操作替换为异步替换的速查表。
图 5 执行操作的“异步方式”
执行以下操作…
替换以下方式…
使用以下方式
检索后台任务的结果
Task.Wait 或 Task.Result
等待任何任务完成
Task.WaitAny
await Task.WhenAny
检索多个任务的结果
Task.WaitAll
await Task.WhenAll
等待一段时间
Thread.Sleep
await Task.Delay
总结这第二个指导原则便是,应避免混合使用异步代码和阻塞代码。&混合异步代码和阻塞代码可能会导致死锁、更复杂的错误处理及上下文线程的意外阻塞。&此指导原则的例外情况是控制台应用程序的 Main 方法,或是(如果是高级用户)管理部分异步的基本代码。
配置上下文
在本文前面,我简要说明了当等待未完成 Task 时默认情况下如何捕获“上下文”,以及此捕获的上下文用于恢复 async 方法的执行。&图 3&中的示例演示在上下文上的恢复执行如何与同步阻塞发生冲突从而导致死锁。此上下文行为还可能会导致另一个问题 — 性能问题。&随着异步 GUI 应用程序在不断增长,可能会发现 async 方法的许多小部件都在使用 GUI 线程作为其上下文。&这可能会形成迟滞,因为会由于“成千上万的剪纸”而降低响应性。
若要缓解此问题,请尽可能等待 ConfigureAwait 的结果。&下面的代码段说明了默认上下文行为和 ConfigureAwait 的用法:
async Task MyMethodAsync()
// Code here runs in the original context.
await Task.Delay(<span style="color: #00);
// Code here runs in the original context.
await Task.Delay(<span style="color: #00).ConfigureAwait(
continueOnCapturedContext: false);
// Code here runs without the original
// context (in this case, on the thread pool).
通过使用 ConfigureAwait,可以实现少量并行性: 某些异步代码可以与 GUI 线程并行运行,而不是不断塞入零碎的工作。
除了性能之外,ConfigureAwait 还具有另一个重要方面: 它可以避免死锁。&再次考虑图 3;如果向 DelayAsync 中的代码行添加“ConfigureAwait(false)”,则可避免死锁。&此时,当等待完成时,它会尝试在线程池上下文中执行 async 方法的剩余部分。&该方法能够完成,并完成其返回任务,因此不存在死锁。&如果需要逐渐将应用程序从同步转换为异步,则此方法会特别有用。
如果可以在方法中的某处使用 ConfigureAwait,则建议对该方法中此后的每个 await 都使用它。&前面曾提到,如果等待未完成的 Task,则会捕获上下文;如果 Task 已完成,则不会捕获上下文。&在不同硬件和网络情况下,某些任务的完成速度可能比预期速度更快,需要谨慎处理在等待之前完成的返回任务。&图 6&显示了一个修改后的示例。
图 6 处理在等待之前完成的返回任务
async Task MyMethodAsync()
// Code here runs in the original context.
await Task.FromResult(<span style="color: #);
// Code here runs in the original context.
await Task.FromResult(<span style="color: #).ConfigureAwait(continueOnCapturedContext: false);
// Code here runs in the original context.
var random = new Random();
int delay = random.Next(<span style="color: #); // Delay is either 0 or 1
await Task.Delay(delay).ConfigureAwait(continueOnCapturedContext: false);
// Code here might or might not run in the original context.
// The same is true when you await any Task
// that might complete very quickly.
如果方法中在 await 之后具有需要上下文的代码,则不应使用 ConfigureAwait。&对于 GUI 应用程序,包括任何操作 GUI 元素、编写数据绑定属性或取决于特定于 GUI 的类型(如 Dispatcher/CoreDispatcher)的代码。&对于 ASP.NET 应用程序,这包括任何使用 HttpContext.Current 或构建 ASP.NET 响应的代码(包括控制器操作中的返回语句)。&图 7&演示 GUI 应用程序中的一个常见模式:让 async 事件处理程序在方法开始时禁用其控制,执行某些 await,然后在处理程序结束时重新启用其控制;因为这一点,事件处理程序不能放弃其上下文。
图 7 让 async 事件处理程序禁用并重新启用其控制
private async void button1_Click(object sender, EventArgs e)
button1.Enabled = false;
// Can't use ConfigureAwait here ...
await Task.Delay(<span style="color: #00);
// Because we need the context here.
button1.Enabled = true;
每个 async 方法都具有自己的上下文,因此如果一个 async 方法调用另一个 async 方法,则其上下文是独立的。&图 8&演示的代码对图 7&进行了少量改动。
图 8 每个 async 方法都具有自己的上下文
private async Task HandleClickAsync()
// Can use ConfigureAwait here.
await Task.Delay(<span style="color: #00).ConfigureAwait(continueOnCapturedContext: false);
private async void button1_Click(object sender, EventArgs e)
button1.Enabled = false;
// Can't use ConfigureAwait here.
await HandleClickAsync();
// We are back on the original context for this method.
button1.Enabled = true;
无上下文的代码可重用性更高。尝试在代码中隔离上下文相关代码与无上下文的代码,并尽可能减少上下文相关代码。在图 8&中,建议将事件处理程序的所有核心逻辑都置于一个可测试且无上下文的 async Task 方法中,仅在上下文相关事件处理程序中保留最少量的代码。即使是编写 ASP.NET 应用程序,如果存在一个可能与桌面应用程序共享的核心库,请考虑在库代码中使用 ConfigureAwait。
总结这第三个指导原则便是,应尽可能使用 Configure-Await。无上下文的代码对于 GUI 应用程序具有最佳性能,是一种可在使用部分 async 基本代码时避免死锁的方法。此指导原则的例外情况是需要上下文的方法。
了解您的工具
关于 async 和 await 有许多需要了解的内容,这自然会有点迷失方向。图 9&是常见问题的解决方案的快速参考。
图 9 常见异步问题的解决方案
创建任务以执行代码
Task.Run 或 TaskFactory.StartNew(不是&Task 构造函数或 Task.Start)
为操作或事件创建任务包装
TaskFactory.FromAsync 或 TaskCompletionSource&T&
CancellationTokenSource 和 CancellationToken
IProgress&T& 和 Progress&T&
处理数据流
TPL 数据流或被动扩展
同步对共享资源的访问
SemaphoreSlim
异步初始化资源
AsyncLazy&T&
异步就绪生产者/使用者结构
TPL 数据流或 AsyncCollection&T&
第一个问题是任务创建。显然,async 方法可以创建任务,这是最简单的选项。如果需要在线程池上运行代码,请使用 Task.Run。如果要为现有异步操作或事件创建任务包装,请使用 TaskCompletionSource&T&。下一个常见问题是如何处理取消和进度报告。基类库 (BCL) 包括专门用于解决这些问题的类型: CancellationTokenSource/CancellationToken 和 IProgress&T&/Progress&T&。异步代码应使用基于任务的异步模式(或称为 TAP,),该模式详细说明了任务创建、取消和进度报告。
出现的另一个问题是如何处理异步数据流。任务很棒,但是只能返回一个对象并且只能完成一次。对于异步流,可以使用 TPL 数据流或被动扩展 (Rx)。TPL 数据流会创建类似于主角的“网格”。Rx 更加强大和高效,不过也更加难以学习。TPL 数据流和 Rx 都具有异步就绪方法,十分适用于异步代码。
仅仅因为代码是异步的,并不意味着就安全。共享资源仍需要受到保护,由于无法在锁中等待,因此这比较复杂。下面是一个异步代码示例,该代码如果执行两次,则可能会破坏共享状态,即使始终在同一个线程上运行也是如此:
Task&int& GetNextValueAsync(int current);
async Task UpdateValueAsync()
value = await GetNextValueAsync(value);
问题在于,方法读取值并在等待时挂起自己,当方法恢复执行时,它假设值未更改。为了解决此问题,使用异步就绪 WaitAsync 重载扩展了 SemaphoreSlim 类。图 10&演示 SemaphoreSlim.WaitAsync。
图 10 SemaphoreSlim 允许异步同步
SemaphoreSlim mutex = new SemaphoreSlim(<span style="color: #);
Task&int& GetNextValueAsync(int current);
async Task UpdateValueAsync()
await mutex.WaitAsync().ConfigureAwait(false);
value = await GetNextValueAsync(value);
mutex.Release();
异步代码通常用于初始化随后会缓存并共享的资源。没有用于此用途的内置类型,但是 Stephen Toub 开发了 AsyncLazy&T&,其行为相当于 Task&T& 和 Lazy&T& 合二为一。该原始类型在其博客 () 上进行了介绍,并且在我的 AsyncEx 库 () 中提供了更新版本。
最后,有时需要某些异步就绪数据结构。TPL 数据流提供了 BufferBlock&T&,其行为如同异步就绪生产者/使用者队列。而 AsyncEx 提供了 AsyncCollection&T&,这是异步版本的 BlockingCollection&T&。
我希望本文中的指导原则和指示能有所帮助。异步真的是非常棒的语言功能,现在正是开始使用它的好时机!
扫码关注微信公众号
原文地址:
随笔 - 233Javascript学习笔记-async函数 - 简书
Javascript学习笔记-async函数
javascript async函数.png
ES7(误)引入的async函数,可以说是Javascript异步编程代码组织方式的又一次升级。使得代码可以通过顺序式的方式来编写异步逻辑。
创建一个async函数有两种方式:function声明和构造函数(和生成器类似)
1.1 async function函数声明
使用async function的声明方式可以创建async函数
// 函数声明
async function f() {
// 也可以使用函数表达式
var f = async function() {
1.2 AsyncFunction构造函数
和Generator一样,也可以直接获得原型链上的构造函数AsyncFunction(...arguments, expression)来创建async函数,其中构造方法最后一个参数为expression函数方法体,其余参数...arguments为函数所需参数
var AsyncFunction = Object.getPrototypeOf(async function(){}).
var f = new AsyncFunction('a', 'return a*10');
async function f(a){
return a*10;
个人觉得还是用async function的方式来创建async函数,简单而且易于开发和维护
直接进行方法调用就可以执行async函数,async函数将返回一个Promise对象。其中Promise的决议值有下面三种情况:
如果是函数方法体中存在return,则返回值将作为决议值
如果没有return,则决议值为undefined
如果方法体中出现异常,则决议值为reject的异常信息
// 有return值,返回值作为决议值
async function f1() {
var af = f1();
console.log(af instanceof Promise); //
af.then(fulfill =& {
console.log(fulfill); // 1
// 没有return,决议值为undefined
(async function() {})().then(fulfill=&{
console.log(fulfill); // undefined
// 抛出异常,决议值为reject的异常信息
(async function() {
throw new Error('error');
})().then(fulfill=&{
console.log(fulfill); // undefined
}, reject =& {
console.log('got the error: ');
console.log(reject);
async函数中可以使用await关键字,和Generator中的yield关键字类似,可以理解为await也能起到暂停的作用,但是await更具有智能性,Generator我们需要手动触发next函数来推动Generator的yield的不断运行,但是使用await将自动实现这一过程,不用再人工进行手动触发,且会将await后表达式自动进行Promise转换并在Promise完成决议后,将决议值返回
// 对于生成器函数和yield
function *g() {
let a = yield new Promise(resolve =& {
setTimeout(_=&{
resolve(1);
console.log(1); // 1s后输出1
var it = g();
it.next().value.then(fulfill =& {
it.next(fulfill); // 手动在回调中调用next,推进iterator的执行
// 使用async函数和await
async function f() {
let a = await new Promise(resolve =& {
setTimeout(_=&{
resolve(1);
console.log(1); // 1s后输出1
从代码可以看出
await关键字会在表达式决议后获得决议值,之后再继续运行代码
使用async函数,将按照同步的方式自动运行我们的代码,我们不用像Generator手动调用next或者使用Generator的runner函数,相当于Generator + Promise + Generator runner组合
async函数可以改变我们异步代码的组织习惯,对于未来的异步代码编写,我们很多时候都可以抛弃异步回调函数的做法了
专注前端开发的developer。
异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本没法用,非卡死不可。 ES6诞生以前,异步编程的方法,大概有下面四种。 回调函数 事件监听 发布/订阅 Promise 对象 ES6将JavaScript异步编...
官方中文版原文链接 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大奖:点击这里领取 在第二章中,我们发现了在使用回调表达异步流程控制时的两个关键缺陷: 基于回调的异步与我们的大脑规划任务的各个步骤的过程不相符。 由于 控制倒转...
弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点就是单线程,而这个线程中拥有唯一的一个事件循环。当然新标准中的web worker涉及到了多线程,但它的原理是利用一个父线程和多个子线程,归根结底来说js仍逃不...
js是单线程的语言,异步对于js的重要性相比于别的语言更大。async/await是ECMAScript 2017的标准,是最新的js异步调用解决方案。async/await标准的出台也不是一蹴而就的,本文尝试追本溯源,以便更好的了解async/await。 异步调用 阮一...
javascript的运行机制是单线程处理,即只有上一个任务完成后,才会执行下一个任务,这种机制也被称为“同步”。 “同步”的最大缺点,就是如果某一任务运行时间较长,其后的任务就无法执行。这样会阻塞页面的渲染,导致页面加载错误或是浏览器不响应进入假死状态。 如果这段任务采用...
如果阳光明媚 那么落一场雨胜 如果宝石蝇营狗苟 那么微醺无所事事胜 如果富士山赏樱花 那么故国明月胜 如果灯下读《被缚的普罗米修斯》 那么普罗米修斯本人胜 如果让我停止想念 那么你胜
今天读到王昌龄的诗,他是唐代诗人,山西太原人,他的七绝成就最高,与李白齐名,号称七绝圣手。 他的七言绝句在内容上可分为三类,都自有特色,有边塞诗第一,闺怨诗第二,第三类是送别诗。 比如著名的《出塞》:秦时明月汉时关,万里长征人未还。但使龙城飞将在,不叫胡马度阴山。 还有一首...
(这是许6569 小能熊365日写作计划第45篇文章) 2009年的冬天,独自去大连,因为想去看看冬天寂寞的大海。 那一天是大年初三。冬天的黄昏,寂静的田野升起淡淡的夜雾。坐在火车里看窗外,能看见广阔灰色的天空。整个车站空荡荡的。 我坐在窗边,凝望天际深浓的暮色。候车厅里零...
今天天气真好!
【日记星球21天蜕变之旅--第19天】
日 阴转小雨 我是日记星球106号星宝宝叮叮,我正在参加日记星球21天蜕变之旅,我承诺每日坚持一篇100字以上的原创日记,如有违反,给每位星宝宝送一本好书。 从日开始到现在,每一个周日...}

我要回帖

更多关于 构造函数可以继承吗 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信