2017-05-06 54 views
0

我正在用setTimeouts在其中構建Promise鏈。所有的承諾都需要串聯運行而不是平行運行。我正在使用Bluebird模塊來實現Promise執行的串行流程。帶有setTimeout的JavaScript序列承諾

有人可以解釋我爲什麼這段代碼給我輸出1,2,3,4而不是4,3,2,1?

var bluebirdPromise = require('bluebird'); 
 

 
function p1(value) { 
 
    return new Promise(function(resolve, reject) { 
 
     setTimeout(function(resolve) { 
 
      console.log(value); 
 
      resolve; 
 
     }, value * 1000); 
 
    }); 
 
} 
 

 
var arr = [p1(4), p1(3), p1(2), p1(1)]; 
 

 
bluebirdPromise.reduce(arr, 
 
    function(item, index, length) { 
 

 
    }).then(function (result) { 
 

 
    });

+1

當您調用'p1'時,您已經開始操作。你的'arr'解決方案不起作用。 – Bergi

+1

'bluebirdpromise.reduce([4,3,2,1],p1)'可能會完成這項工作(一旦你糾正了'p1',就像trincot所詳述的那樣) – Bergi

回答

1

有幾個問題:

  • console.log你已經不言依賴於以前的解決承諾。只有超時決定輸出會發生。當您在「相同」時間創建所有四個承諾時,因此所有四個setTimeout調用被同時調用,它們的回調將在確定的超時時調用。不管你如何鏈接承諾......爲了解決這個問題,你需要將console.log移回then的回調函數,因爲回調函數只有在鏈中的前一個promise被解決時纔會執行。

  • resolve函數未在代碼中調用。你需要添加括號。

  • setTimeout回調的決心參數隱藏真正功能具有相同名稱:您需要刪除該參數。

這裏是建議的更正。對於這個片段我已經取代藍鳥reduce與標準Array#reduce,但它會與藍鳥的reduce類似的工作:如果你有一個承諾,創作者功能,p

function p1(value) { 
 
    return new Promise(function(resolve, reject) { 
 
     setTimeout(function() { // *** 
 
      resolve(value); // *** 
 
     }, value * 1000); 
 
    }); 
 
} 
 

 
var arr = [p1(4), p1(3), p1(2), p1(1)]; 
 

 
arr.reduce(function(promise, next) { 
 
    return promise.then(_ => next).then(value => { 
 
     console.log(value); // *** 
 
     return value; 
 
    }); 
 
}, Promise.resolve());

0

,並希望要運行序列號爲的序列,則不需要使用承諾來加載數組,而只需讓它成爲數組的常規數組即可

注意我在這裏並沒有使用value * 1000 - 在你的代碼中,你認爲你必須人爲地編排使用計算的setTimeout延遲以特定順序觸發的承諾;事實並非如此。仔細觀察下面代碼的評估,看看我們在每個承諾和.then之間有一個1秒的延遲是否保持順序

還要注意,這個代碼將在第一個承諾解決後立即開始輸出 - 而不是等待所有的承諾,所有輸出值

const p = x => 
 
    new Promise(f => 
 
    setTimeout(f, 1e3, x)) 
 
    
 
const arr = [4,3,2,1] 
 

 
arr.reduce((acc, x) => 
 
    acc.then(() => p(x)).then(console.log), Promise.resolve())

確定之前解決,讓你有這些承諾在串行順序運行,但爲什麼呢?除非後面的步驟以某種方式取決於先前步驟的結果,否則沒有理由爲什麼要放慢速度 - 即每個承諾的結果不依賴於其他步驟,因此儘可能快地計算它們。但是你擔心訂單會丟失,對嗎?別擔心,一切都會好的 - 我甚至使用隨機延遲向您展示的是每個時間承諾採取未盡事宜

const p = x => 
 
    new Promise(f => 
 
    setTimeout(f, 1e3 * Math.random(), x)) 
 
    
 
const arr = [4,3,2,1] 
 

 
arr.map(p).reduce((acc, x) => 
 
    acc.then(() => x).then(console.log), Promise.resolve())

所以,現在,所有的承諾都可以立即開始,並且輸出將在第一個承諾解決後立即開始(不同於Promise.all,這將等待所有承諾在任何值可用之前完成)。

我只提到這是一個替代方案,因爲您提供的示例沒有顯示實際的需要,以便序列執行承諾。您可能天真地簡化了您的問題的域名,但只有您知道是否屬於這種情況。