2016-01-10 47 views
3

我有一個我想要提出Web請求的網址陣列。我想以承諾的方式異步調用Web請求,並繼續鏈接以完成任何承諾。現在我做當任何承諾完成時,是否有可能履行承諾?

.then((lines) => { 
    var tasks = []; 
    lines.forEach((line) => { 
     tasks.push(() => { return rp(BASE_URL + line); }); 
    }); 
    return Promise.map(tasks, (job) => { return job(); }, { concurrency: 5 }); 
}) 
.then((info) => { 
    inspect('info: ' + info); 
}) 

然而,與此有關的問題是,它等待所有任務調用next .then()

我想知道是否可以調用。那麼()對任何前完成任務已經解決,並繼續鏈?

我猜我可以這樣做:

.then((lines) => { 
    lines.forEach((line) => { 
     rp(BASE_URL + line) 
      .then((info) => { 
       inspect(info); 
      }); 
    }); 
}) 
.then((a) => { 
    // all requests have been made 
}) 

這開始像地獄的回調,因爲我嵌套我。那麼()的,將可能需要在其他一些情況下,這樣做我遇到同樣的問題。

我所尋找的是這樣的:

.then((lines) => { 
    lines.forEach((line) => { 
     yield return rp(BASE_URL + line); 
    }); 
}) 
.then((info) => { 
    // will call inspect on each yield 
    inspect('info: ' + info); 
}) 
+0

您可以實現自己的類型承諾。也許將它命名爲PromiseGroup或PromiseList。這種類型的對象將代表任意數量的承諾。然後你可以編寫例如(line => rp(BASE_URL + line))。then(info => inspect('info:'+ info))' – SpiderPig

+2

你的「回撥地獄」版本是正確的。 Promise使你不必爲多次回調而保留嵌套縮進級別,但是如果你需要做的事情不在鏈的「最高級別」,嵌套的promise應該沒問題 –

+0

看到Benjamin的解決方案後,我必須說Obserbles看起來像一個很棒的解決所以也許你可以有一個名爲'arrayToObservable'的函數或類似的東西來將'lines'轉換爲可觀察的。 – SpiderPig

回答

0

你可以整理你的回調地獄版位,使用.map.each降低部分嵌套層次

.map((line) => { 
    return rp(BASE_URL + line); 
}, {concurrency: 5}) 
.each((info) => 
    inspect(info); 
}) 
.then((a) => { 
    // all requests have been made 
}) 

注這確實與你的版本有些不同。只有在完成所有請求後,.each回調纔會運行。轉向基於流的處理模型可以避免這種情況。

1

我有一個網址的數組,我想做一個網絡請求。我想以承諾的方式異步調用Web請求,並繼續鏈接以完成任何承諾。

你會在同步代碼中做到這一點? Promise模擬同步代碼。

你就會把處理函數中的整個單請求,然後map上的邏輯:

.map(processLine); 

function processLine(line) { 
    return rp(BASE_URL + line).then(inspect); 
} 

明白,這是不是一個假象是很重要的,但的一個核心性能承諾代理模式。承諾代表像回調這樣的單一任務 - 而不是事件發射器這樣的複雜任務。

如果您有興趣,您可能需要考慮一個可觀察或更具反應性的系統,您也可以使用異步迭代器(實際上是您所需的語法)。我使用的觀測,因爲他們更容易在這裏使用,但兩者會工作:

new Observable(obs => { 
    let reqs = lines.map(x => rp(BASE_URL + line)); 
    let counter = reqs.length; 
    for(let req of reqs) { 
     req.then(v => { 
      obs.onNext(v); 
      if(!--counter) obs.onCompleted(); 
     }); 
    } 
}).map(u => { // every new value is available here 
    console.log(u); // use flatMap and return a promise if you want 
        // promise like chaining behavior 
}); 
+0

不應該是'if(! - counter)'或'if( - counter === 0)'? – SpiderPig

+0

良好的捕獲,應該可能是'Observable.from(lines).map(x => rp(BASE_URL + line))。flatMap(x => x)'而不是顯式構造。 –

0

你的「回調地獄」是不是地獄可言,而是並行運行多個異步鏈的優雅的方式,那裏的東西是不同步的。

你錯過了你的版本中的關鍵return然而,爲什麼我喜歡隱式返回箭頭函數{} - 而你忽略了映射。

在ES6我會怎麼做:

.then(lines => Promise.all(lines.map(line => 
    rp(BASE_URL + line).then(info => inspect(info))))); 
}) 
.then(allInspectResults => { 
    // all requests and inspections have been made 
}) 
.catch(e => console.error(e)); 
+0

這似乎從我試圖避免的基本問題受到影響。如果rp()需要很長時間,但我需要從那個結果發出另一個阻塞調用,然後我需要從那個結果發出另一個阻塞調用,等等,然後我必須等待所有的a在我打電話給b之前完成,然後在我可以打電話給c等之前等待所有的b完成。我需要能夠打電話給b(a)當a的其中一個項目完成時。 – esac

+0

'rp'是異步的,對吧?然後沒有阻塞。假設有5行,那麼'rp()'將會一個接一個地被調用5次,並且希望每個調用都會立即返回,然後立即調用'inspect'調用,直到您有5個promise。從這一點開始,等待所有5件事情完成的唯一事情是'allInspectResults'。 5個'inspect'調用中的每一個都僅取決於來自相應的'rp'調用的許諾,它們將被調用的順序取決於哪個'rp'調用首先完成。 「rp」調用的異步部分並行運行。 – jib