#javascriptの場合
javascript には Promise
という、コールバック地獄を緩和する仕組みが(最近は)あります。
例えば、 setTimeout
はコールバックで指定したfunctionを呼んでくれます。
setTimeout( function(){ console.log("Hello");} , 5000); // 5000ミリ秒後に "Hello" と出力される
「じゃぁ、5秒後に"Hello"って出力した後、さらに4秒後に"good"って出力して、さらに3秒後に"by"って出したいな」
setTimeout(function() {
console.log("Hello");
setTimeout(function() {
console.log("good");
setTimeout(function() {
console.log("by");
},3000); //3000ミリ秒後に "by" と出力される
},4000); //4000ミリ秒後に "good" と出力される
}, 5000); // 5000ミリ秒後に "Hello" と出力される
はい。死ねますね。 これが俗にいう「コールバック地獄」というやつです。
これを、Proimse
でラップしてあげる(+最近のJavaScriptならasync~awaitで待ち受けたりすると)
function sleep(waitms) {
return new Promise(function(resolve)
{
setTimeout(function() {
resolve();
}, waitms);
});
}
async function Test(){
await sleep(5000); //中で発火される setTimeout が 5000ms後にPromiseのresolve()を呼ぶ=終了とみなす
console.log("Hello");
await sleep(4000);
console.log("good");
await sleep(3000);
console.log("by");
}
はい。めっちゃ見やすくなりましたね。
#C#の場合
非同期の結果通知がコールバックしかないような処理をTaskとして待ち受けられる Promiseクラスを作ってみました。
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
public class Promise<TResult> : IDisposable
{
public delegate void PromiseResolve(Action<TResult> resolve);
public delegate void PromiseResolveReject(Action<TResult> resolve, Action<Exception> reject);
private readonly TaskCompletionSource<TResult> tcs;
private readonly PromiseResolve unRegistResolve;
private readonly PromiseResolveReject unRegistResolveReject;
public Promise(PromiseResolveReject registCallback, CancellationToken ct = default) : this(registCallback, null, ct)
{
}
public Promise(PromiseResolve registCallback, CancellationToken ct = default) : this(registCallback, null, ct)
{
}
public Promise(PromiseResolveReject registCallback, PromiseResolveReject unRegistCallback,
CancellationToken ct = default)
{
tcs = new TaskCompletionSource<TResult>();
if (ct != default) ct.Register(() => tcs.TrySetCanceled(ct));
registCallback(Resolve, Reject);
unRegistResolveReject = unRegistCallback;
}
public Promise(PromiseResolve registCallback, PromiseResolve unRegistCallback, CancellationToken ct = default)
{
tcs = new TaskCompletionSource<TResult>();
if (ct != default) ct.Register(() => tcs.TrySetCanceled(ct));
registCallback(Resolve);
unRegistResolve = unRegistCallback;
}
public void Dispose()
{
if (unRegistResolve != null)
unRegistResolve(Resolve);
else
unRegistResolveReject?.Invoke(Resolve, Reject);
}
public TaskAwaiter<TResult> GetAwaiter()
{
return tcs.Task.GetAwaiter();
}
private void Resolve(TResult result)
{
tcs.TrySetResult(result);
}
private void Reject(Exception ex)
{
tcs.TrySetException(ex);
}
}
イベントの追加と削除のあたりがなんかダメっぽいですね・・・。