2016-06-21 44 views
1

我正在經歷雄辯的JavaScript並且必須實現promise.all。這是我的解決方案。promise.all的JavaScript實現不工作?

function all(promises) { 
    return new Promise(function(success, fail) { 
    var results = []; 
    var failed = false; 
    promises.forEach(function(promise) { 
     promise.then(function(result) { 
     results.push(result); 
     }, function (error) { 
     failed = true; 
     fail(error); 
     }); 
    }); 
    if (!failed) 
     success(results); 
    }); 
} 

下面是我正在運行它的測試。

// Test code. 
all([]).then(function(array) { 
    console.log("This should be []:", array); 
}); 
function soon(val) { 
    return new Promise(function(success) { 
    setTimeout(function() { success(val); }, 
       Math.random() * 500); 
    }); 
} 
all([soon(1), soon(2), soon(3)]).then(function(array) { 
    console.log("This should be [1, 2, 3]:", array); 
}); 
function fail() { 
    return new Promise(function(success, fail) { 
    fail(new Error("boom")); 
    }); 
} 
all([soon(1), fail(), soon(3)]).then(function(array) { 
    console.log("We should not get here"); 
}, function(error) { 
    if (error.message != "boom") 
    console.log("Unexpected failure:", error); 
}); 

我的代碼顯然是錯誤的監守它的輸出

這應該是[] []

這應該是[1,2,3]:[]

我們應該不在這裏

第一個是唯一正確的。 實際的解決方案,它是從我的缺陷觀爲我寫的作品,並可以在這裏找到基本相同: http://eloquentjavascript.net/code/#17.2

爲什麼我的代碼不能正常工作?它出什麼問題了?

+3

@AliTorabi不要是個混蛋。有時重新發明輪子以更好地理解編程和您正在使用的語言是很重要的。 –

+0

@AliTorabi作爲一種學習練習,我相信重新實現常用功能以更好地理解它們是一件好事。我的理解是,這是OP的目標。 – Timo

+0

我不再是混蛋。處理代碼和問題 –

回答

0

你是達到

if (!failed)     
    success(results); 

如此之快。如果所有的承諾都完成了並且是最後一個承諾,那麼一切都已經解決了。

function all(promises) { 
    return new Promise(function(success, fail) { 
     var results = []; 
     var failed = false; 
     promises.forEach(function(promise) { 
      promise.then(function(result) { 

       results.push(result); 

       if (results.length == promises.length) { 
        if (!failed) 
         success(results); 
       } 
      }, function (error) { 

       failed = true; 
       fail(error); 
      }); 
     }); 


    }); 
} 

測試你的函數:

all([soon(1), fail(), soon(3)]).then(function(array) { 
    console.log("We should not get here"); 
}, function(error) { 
    console.log("Fail"); 
    if (error.message != "boom") 
     console.log("Unexpected failure:", error); 
}); 

但是,它仍然有欠缺的結果排序。

+0

除了缺少訂單,它也不適用於空輸入。 – Bergi

1

forEach是同步的,if (!failed)語句將在承諾解決之前運行。你可以做到以下幾點:

function all(promises) { 
    var results = [], 
     left = promises.length; 
    if (!left) { 
    return Promise.resolve(results) 
    } 
    return new Promise((res, rej) => { 
    promises.forEach((p, i) => { 
     Promise.resolve(p).then(x => { 
     results[i] = x 
     left -= 1 
     if (left === 0) { 
      res(results) 
     } 
     }, rej) 
    }) 
    }) 
} 

使用稀疏數組,你可以保持這個順序,例如:

function timeout(n) { 
    return new Promise((res) => { 
    setTimeout(() => { 
     res(n) 
    }, n) 
    }) 
} 

var pa = timeout(200) 
var pb = timeout(300) 
var pc = timeout(100) 

// success 
all([pa, pb, pc]).then(console.log).catch(console.log) //=> [200, 300, 100] 


// error 
var pd = Promise.reject('error') 

all([pa, pb, pc, pd]).then(console.log).catch(console.log) //=> error 
+0

只有最後一次承諾能夠滿足時,這似乎才起作用。它也不適用於空輸入。我推薦'.then(...,rej)'爲了簡單而沒有'catch' – Bergi

+0

啊,你說得對@Bergi,太糟糕了。 – elclanrs

+0

看到我的編輯,希望這次覆蓋所有情況。 – elclanrs

0

正如其他人所說,你.forEach完成立即,甚至在你的承諾已經解決。對於您的.forEach中的每個承諾,完成異步計算需要一段隨機時間。對於此exercise,時間爲Math.random() * 5000

因此,在每次迭代中最終的結果是「承諾」,並且.forEach繼續。這就是爲什麼你得到一個空數組:[]。在你的任何承諾解決之前,.forEach已經完成。

你想要做的就是跟蹤解決的每一個承諾,只要你有三個已解決的承諾,你可以在results數組上調用你的success處理程序。您可以通過在每個承諾的.then()傳遞的處理程序之外保留一個閉包變量來做到這一點。當每個承諾完成時,您可以增加該變量。在我的解決方案中,我將這個變量命名爲count

function all(promises) { 
    return new Promise(function(success, fail) { 
    if (promises.length === 0) { 
     return success([]); 
    } 
    const results = []; 
    let count = 0; 

    for (let i = 0; i < promises.length; i++) { 
     promises[i].then(val => { 
     results[i] = val; 
     count++; 

     if (count === promises.length) { 
      return success(results); 
     } 
     }).catch(err => fail(err)); 
    } 
    }); 
}