promiseチェーンでsetTimeoutを使用する


115

ここで私は約束に頭を回そうとしています。最初のリクエストで一連のリンクをフェッチします。次のリクエストで最初のリンクのコンテンツをフェッチしますが、次のプロミスオブジェクトを返す前に遅延を作りたいので、 setTimeout on it。しかし、次のJSONエラー(without setTimeout() it works just fine)が表示されます

SyntaxError:JSON.parse:JSONデータの1行1列目の予期しない文字

なぜ失敗するのか知りたいのですが?

let globalObj={};
function getLinks(url){
    return new Promise(function(resolve,reject){

       let http = new XMLHttpRequest();
       http.onreadystatechange = function(){
            if(http.readyState == 4){
              if(http.status == 200){
                resolve(http.response);
              }else{
                reject(new Error());
              }
            }           
       }
       http.open("GET",url,true);
       http.send();
    });
}

getLinks('links.txt').then(function(links){
    let all_links = (JSON.parse(links));
    globalObj=all_links;

    return getLinks(globalObj["one"]+".txt");

}).then(function(topic){


    writeToBody(topic);
    setTimeout(function(){
         return getLinks(globalObj["two"]+".txt"); // without setTimeout it works fine 
         },1000);
});

1
これreturnは関数固有であり、親関数にのみ戻ります。また、非同期メソッドから戻ることはできません。
アデネオ2016

2
このコードを構造化するには、を使用するよりもはるかに優れた方法があることに注意してくださいglobalObj
Bergi

どこにJSON.parse投げますか?setTimeout1つのthenコールバックがあるかどうかが前のthenコールバックの呼び出しに影響を与えるとは信じがたいです。
Bergi

回答:


191

promiseチェーンを継続するsetTimeout()ために、.then()ハンドラーからpromiseを返さないため、以前のように使用することはできません- setTimeout()役に立たないコールバックからそれを返します。

代わりに、次のような単純な小さな遅延関数を作成できます。

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

そして、それを次のように使用します:

getLinks('links.txt').then(function(links){
    let all_links = (JSON.parse(links));
    globalObj=all_links;

    return getLinks(globalObj["one"]+".txt");

}).then(function(topic){
    writeToBody(topic);
    // return a promise here that will be chained to prior promise
    return delay(1000).then(function() {
        return getLinks(globalObj["two"]+".txt");
    });
});

ここでは、.then()ハンドラーからpromiseを返しているため、適切にチェーンされています。


また、Promiseオブジェクトにdelayメソッドを追加して、次のようにPromiseのメソッドを直接使用することもでき.delay(x)ます。

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

Promise.prototype.delay = function(t) {
    return this.then(function(v) {
        return delay(t, v);
    });
}


Promise.resolve("hello").delay(500).then(function(v) {
    console.log(v);
});

または、すでにメソッドが組み込まれているBluebird promiseライブラリを使用.delay()します。


1
resolve関数はthen().. so内の関数です。setTimeout(resolve、t)は、setTimeout(function(){return ....}、t)を意味します...それで、なぜ機能するのですか?
AL-zami 2016

2
@ AL-zami-のdelay()後で解決されるプロミスを返しますsetTimeout()
jfriend00 2016

setTimeoutのpromiseラッパーを作成して、promiseを簡単に遅延させました。github.com/zengfenfei/delay
Kevin

4
@pdem- v遅延プロミスで解決し、プロミスチェーンを渡すオプションの値です。 resolve.bind(null, v)代わりに、あるfunction() {resolve(v);} 動作しますどちらか。
jfriend00

どうもありがとう...プロトタイプの遅延は機能しましたが、関数>>> .thenステートメントは機能しませんでした。tは未定義でした。
クリスチャンマシュー

75
.then(() => new Promise((resolve) => setTimeout(resolve, 15000)))

更新:

非同期関数でスリープが必要なとき

await new Promise(resolve => setTimeout(resolve, 1000))

そのような非同期関数内でただ眠ることができませんでしたか?新しいPromise(resolve => setTimeout(resolve、1000));を待つ
アンソニームーンビームトーリー

@AnthonyMoonBeamToorie修正済み、ty
イゴールコルサコフ

52

答えの短いES6バージョン:

const delay = t => new Promise(resolve => setTimeout(resolve, t));

そして、あなたは行うことができます:

delay(3000).then(() => console.log('Hello'));

rejecteslint検証などのオプションが必要な場合const delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas

10

.then()ブロック内にいて、settimeout()を実行したい場合

            .then(() => {
                console.log('wait for 10 seconds . . . . ');
                return new Promise(function(resolve, reject) { 
                    setTimeout(() => {
                        console.log('10 seconds Timer expired!!!');
                        resolve();
                    }, 10000)
                });
            })
            .then(() => {
                console.log('promise resolved!!!');

            })

出力は以下のようになります

wait for 10 seconds . . . .
10 seconds Timer expired!!!
promise resolved!!!

ハッピーコーディング!


-1

node.jsでは、次のことも実行できます。

const { promisify } = require('util')
const delay = promisify(setTimeout)

delay(1000).then(() => console.log('hello'))

私はこれを試し、無効な数の引数を得ました、遅延関数内で0が期待されました。
Alex Rindone

node.js 8、10、12、13で機能することを確認できます。コードの実行方法はわかりませんが、util誤ってポリフィルされていると想定できます。バンドラーか何かを使用していますか?
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.