これは現実の問題ではありません。約束がどのように作成されるかを理解しようとしています。
SetTimeoutのような何も返さない関数の約束をする方法を理解する必要があります。
私が持っていると仮定します:
function async(callback){
setTimeout(function(){
callback();
}, 5000);
}
async(function(){
console.log('async called back');
});
async
がcallback()
の準備ができた後にsetTimeout
が返せる約束を作成するにはどうすればよいですか?
私はそれをラップするとどこかに連れて行くと思っていました:
function setTimeoutReturnPromise(){
function promise(){}
promise.prototype.then = function() {
console.log('timed out');
};
setTimeout(function(){
return ???
},2000);
return promise;
}
しかし、これ以上考えることはできません。
ここ2017年、PromiseはJavaScriptに組み込まれ、ES2015仕様によって追加されました(IE8-IE11のような古い環境ではポリフィルが利用可能です)。彼らが行った構文は、Promise
コンストラクター(Promise
executor)に渡すコールバックを使用します。このコールバックは、promiseを解決/拒否するための関数を受け取ります引数。
まず、 async
がJavaScript で意味を持つようになったため(特定のコンテキストではキーワードにすぎません)、名前にlater
を使用します混乱を避けるための関数の。
ネイティブプロミス(または忠実なポリフィル)を使用すると、次のようになります。
function later(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
ブラウザの定義 に準拠するsetTimeout
のバージョンを前提としていることに注意してください 。setTimeout
は、後に指定しない限りコールバックに引数を渡しません。 interval(これはブラウザ以外の環境では当てはまらない場合があり、Firefoxでは以前は当てはまらなかったが、現在はChromeでも当てはまり、IE8でも当てはまる)。
遅延後にsetTimeout
に追加の引数を与え、呼び出されたときにそれらをコールバックに渡すことができる漠然と近代的なブラウザで、オプションで解像度値をオプションで渡す場合は、これを実行できます(現在FirefoxおよびChrome; IE11 +、おそらくEdge;notIE8またはIE9、IE10についての考えなし):
function later(delay, value) {
return new Promise(function(resolve) {
setTimeout(resolve, delay, value); // Note the order, `delay` before `value`
/* Or for outdated browsers that don't support doing that:
setTimeout(function() {
resolve(value);
}, delay);
Or alternately:
setTimeout(resolve.bind(null, value), delay);
*/
});
}
ES2015 +の矢印関数を使用している場合は、より簡潔にすることができます。
function later(delay, value) {
return new Promise(resolve => setTimeout(resolve, delay, value));
}
あるいは
const later = (delay, value) =>
new Promise(resolve => setTimeout(resolve, delay, value));
タイムアウトをキャンセルできるようにする場合、later
からプロミスを返すことはできません。プロミスはキャンセルできないためです。
しかし、cancel
メソッドとpromiseのアクセサーを使用してオブジェクトを簡単に返し、キャンセル時にpromiseを拒否できます。
const later = (delay, value) => {
let timer = 0;
let reject = null;
const promise = new Promise((resolve, _reject) => {
reject = _reject;
timer = setTimeout(resolve, delay, value);
});
return {
get promise() { return promise; },
cancel() {
if (timer) {
clearTimeout(timer);
timer = 0;
reject();
reject = null;
}
}
};
};
実例:
const later = (delay, value) => {
let timer = 0;
let reject = null;
const promise = new Promise((resolve, _reject) => {
reject = _reject;
timer = setTimeout(resolve, delay, value);
});
return {
get promise() { return promise; },
cancel() {
if (timer) {
clearTimeout(timer);
timer = 0;
reject();
reject = null;
}
}
};
};
const l1 = later(100, "l1");
l1.promise
.then(msg => { console.log(msg); })
.catch(() => { console.log("l1 cancelled"); });
const l2 = later(200, "l2");
l2.promise
.then(msg => { console.log(msg); })
.catch(() => { console.log("l2 cancelled"); });
setTimeout(() => {
l2.cancel();
}, 150);
通常、Promiseライブラリー(自分で作成するライブラリー、またはそこにあるいくつかのライブラリー)があります。通常、そのライブラリには、作成して後で「解決」できるオブジェクトがあり、そのオブジェクトから取得できる「約束」があります。
そうすると、later
は次のようになります。
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
質問に対するコメントで、私は尋ねました:
独自のpromiseライブラリを作成しようとしていますか?
あなたは言った
私はそうではありませんでしたが、今では実際に私が理解しようとしていたことだと思います。図書館がそれを行う方法
その理解を助けるために、ここに非常に基本的な例がありますが、これはリモートではPromises-Aに準拠していません: Live Copy
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Very basic promises</title>
</head>
<body>
<script>
(function() {
// ==== Very basic promise implementation, not remotely Promises-A compliant, just a very basic example
var PromiseThingy = (function() {
// Internal - trigger a callback
function triggerCallback(callback, promise) {
try {
callback(promise.resolvedValue);
}
catch (e) {
}
}
// The internal promise constructor, we don't share this
function Promise() {
this.callbacks = [];
}
// Register a 'then' callback
Promise.prototype.then = function(callback) {
var thispromise = this;
if (!this.resolved) {
// Not resolved yet, remember the callback
this.callbacks.Push(callback);
}
else {
// Resolved; trigger callback right away, but always async
setTimeout(function() {
triggerCallback(callback, thispromise);
}, 0);
}
return this;
};
// Our public constructor for PromiseThingys
function PromiseThingy() {
this.p = new Promise();
}
// Resolve our underlying promise
PromiseThingy.prototype.resolve = function(value) {
var n;
if (!this.p.resolved) {
this.p.resolved = true;
this.p.resolvedValue = value;
for (n = 0; n < this.p.callbacks.length; ++n) {
triggerCallback(this.p.callbacks[n], this.p);
}
}
};
// Get our underlying promise
PromiseThingy.prototype.promise = function() {
return this.p;
};
// Export public
return PromiseThingy;
})();
// ==== Using it
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
display("Start " + Date.now());
later().then(function() {
display("Done1 " + Date.now());
}).then(function() {
display("Done2 " + Date.now());
});
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
</script>
</body>
</html>