2013-11-20 37 views
63

我有一系列需要按順序運行的承諾。如何按順序執行承諾數組?

var promises = [promise1, promise2, ..., promiseN]; 

調用RSVP.all將並行執行它們:

RSVP.all(promises).then(...); 

但是,我怎麼能運行它們的順序?

我可以手動堆疊起來這樣

RSVP.resolve() 
    .then(promise1) 
    .then(promise2) 
    ... 
    .then(promiseN) 
    .then(...); 

但問題是,承諾的數目而變化並且應許的陣列被動態創建的。

+0

從我的其他答案和downvotes,似乎更多的人需要閱讀[rsvp自述](https://github.com/tildeio/rsvp.js?utm_source=javascriptweekly)它解釋「真的很棒一部分來自你從第一個處理程序返回一個承諾「。如果你不這樣做,你真的錯過了承諾的表達力量。 –

+0

類似的問題,但不是框架特定的:http://stackoverflow.com/q/24586110/245966 –

回答

110

如果你已經有他們在一個數組中,那麼他們已經在執行。如果你有承諾,那麼它已經在執行。這不是承諾的問題(I.E他們不像C#Task在這方面與.Start()方法)。 .all不會執行任何事情 它只是返回一個承諾。

如果你有承諾,返回功能的陣列:

var tasks = [fn1, fn2, fn3...]; 

tasks.reduce(function(cur, next) { 
    return cur.then(next); 
}, RSVP.resolve()).then(function() { 
    //all executed 
}); 

或值:

var idsToDelete = [1,2,3]; 

idsToDelete.reduce(function(cur, next) { 
    return cur.then(function() { 
     return http.post("/delete.php?id=" + next); 
    }); 
}, RSVP.resolve()).then(function() { 
    //all executed 
}); 
+2

這是一個很好的方式來構建一個不需要參數的同質承諾樹。這與使用next_promise指針自己構建樹是完全等價的,如果一組promise對於參數而言不是同類的,那麼您需要做的就是這樣。只是reduce函數正在執行指向當前指針一點點爲你​​。如果你的一些事情可能同時發生,你也會想建立自己的樹。在承諾樹中,分支是序列,葉子是併發的。 –

+0

謝謝你的回答。你是正確的,創造一個承諾已經意味着它正在執行,所以我的問題沒有被正確地形成。我最終以不同的方式解決我的問題。 – jaaksarv

+0

謝謝你。不熟悉「減少」的細節,花了我一段時間才瞭解第一個「曲線」是已解決的承諾。基於此,我設法做出了一個承諾,直到其中一個成功(https://gist.github.com/greggman/0b6eafb335de4bbb557c)。只是在這裏添加它,以免有人發現它有用。 – gman

4

在回答第二次嘗試中,我嘗試更加說明:

首先,一些必要的背景,從RSVP README

當您從第一個處理程序返回一個承諾時,真正可怕的部分會出現......這允許您展開嵌套的回調,並且是承諾的主要功能,可防止具有大量異步代碼的程序中的「向右漂移」 。

這正是你如何使承諾連續的,通過返回承諾之前的承諾then

把這樣一組承諾想象成一棵樹是很有幫助的,其中分支表示順序進程,葉子表示併發進程。

構建這種承諾樹的過程類似於構建其他樹種的常見任務:維護指針或引用您目前正在添加樹枝的樹中的位置,並迭代添加事物。

正如@Esailija在他的回答中指出的那樣,如果您有一組不帶參數的promise返回函數,您可以使用reduce爲您整齊地構建樹。如果你曾經爲自己實施過減免,那麼你會明白@ Esailija的回答在幕後做了什麼減少,是保持對當前承諾的參考(cur),並且每個承諾都會在其then中返回下一個承諾。

如果你沒有一個很好的齊次數組(相對於它們返回的參數),答應返回函數,或者如果你需要一個比簡單線性序列更復雜的結構,你可以構造

var root_promise = current_promise = Ember.Deferred.create(); 
// you can also just use your first real promise as the root; the advantage of 
// using an empty one is in the case where the process of BUILDING your tree of 
// promises is also asynchronous and you need to make sure it is built first 
// before starting it 

current_promise = current_promise.then(function(){ 
    return // ...something that returns a promise...; 
}); 

current_promise = current_promise.then(function(){ 
    return // ...something that returns a promise...; 
}); 

// etc. 

root_promise.resolve(); 

您可以通過使用RSVP.all添加多個「葉子」的建設同時或連續工藝的組合:通過保持在許樹要添加新的承諾,位置的參考你的承諾承諾「分支」。我低調的存在 - 太複雜的答案就是一個例子。

你也可以使用Ember.run.scheduleOnce('afterRender')來確保在下一個承諾被觸發之前在一個承諾中完成的事情被呈現 - 我的downvoted-for-too-複雜的答案也顯示那個例子。

+2

這個好多了,但是我覺得你還在偏離主題。許多承諾答案很常見,人們似乎沒有花時間閱讀這個問題,而只是評論他們個人理解的承諾的某些方面。原來的問題並不涉及並行執行,甚至不是一點點,它清楚地表明,只需通過'then'鏈接就可以了,你已經提供了很多額外的信息,它隱藏了被問到的問題的答案。 –

+0

@DavidMcMullin「....它清楚地表明,只需通過鏈接就可以了......」但實際上他聲明承諾序列是動態構建的。所以他需要了解如何構建樹,即使在這種情況下它是樹「線性序列」的簡單子集。您仍然必須通過參考鏈中的最後一個承諾並添加新的承諾來構建它。 –

+0

當OP表示「承諾數量不一致,承諾數組動態構建」時,我非常確定所有他/她的意思是數組的大小不是預先確定的,因此他/她不能使用簡單的'Promise.resolve()。then(...)。then(...)...',並不是數組正在增長,同時承諾正在執行。當然,現在都沒有實際意義。 – JLRishe

0

我打算在這裏留下這個答案,因爲這會幫助我,當我到這裏尋找解決方案時,我的問題。

我之後的事情基本上是mapSeries ......我碰巧是映射保存在一組值的......我想要的結果...

所以,這裏就我,FWIW,幫助別人尋找在未來類似的事情....

(請注意,上下文是灰燼應用程序)

App = Ember.Application.create(); 

App.Router.map(function() { 
    // put your routes here 
}); 

App.IndexRoute = Ember.Route.extend({ 
    model: function() { 
      var block1 = Em.Object.create({save: function() { 
       return Em.RSVP.resolve("hello"); 
      }}); 
    var block2 = Em.Object.create({save: function() { 
      return Em.RSVP.resolve("this"); 
     }}); 
    var block3 = Em.Object.create({save: function() { 
     return Em.RSVP.resolve("is in sequence"); 
    }}); 

    var values = [block1, block2, block3]; 

    // want to sequentially iterate over each, use reduce, build an array of results similarly to map... 

    var x = values.reduce(function(memo, current) { 
     var last; 
     if(memo.length < 1) { 
      last = current.save(); 
     } else { 
      last = memo[memo.length - 1]; 
     } 
     return memo.concat(last.then(function(results) { 
      return current.save(); 
     })); 
    }, []); 

    return Ember.RSVP.all(x); 
    } 
}); 
6

隨着2017年的ECMAScript異步功能,這將這樣進行:

async function executeSequentially() { 
    const tasks = [fn1, fn2, fn3] 

    for (const fn of tasks) { 
     await fn() 
    } 
} 

您可以使用BabelJS使用異步函數現在

0

我有類似的問題,我做了它運行功能也一一依次遞歸函數。

var tasks = [fn1, fn2, fn3]; 

var executeSequentially = function(tasks) { 
    if (tasks && tasks.length > 0) { 
    var task = tasks.shift(); 

    return task().then(function() { 
     return executeSequentially(tasks); 
    }); 
    } 

    return Promise.resolve(); 
}; 

如果你需要從這些功能收集輸出:

var tasks = [fn1, fn2, fn3]; 

var executeSequentially = function(tasks) { 
    if (tasks && tasks.length > 0) { 
    var task = tasks.shift(); 

    return task().then(function(output) { 
     return executeSequentially(tasks).then(function(outputs) { 
     outputs.push(output); 

     return Promise.resolve(outputs); 
     }); 
    }); 
    } 

    return Promise.resolve([]); 
}; 
0

一切都需要解決一個for循環:)

var promises = [a,b,c]; 
var chain; 

for(let i in promises){ 
    if(chain) chain = chain.then(promises[i]); 
    if(!chain) chain = promises[i](); 
} 

function a(){ 
    return new Promise((resolve)=>{ 
    setTimeout(function(){ 
     console.log('resolve A'); 
     resolve(); 
    },1000); 
    }); 
} 
function b(){ 
    return new Promise((resolve)=>{ 
    setTimeout(function(){ 
     console.log('resolve B'); 
     resolve(); 
    },500); 
    }); 
} 
function c(){ 
    return new Promise((resolve)=>{ 
    setTimeout(function(){ 
     console.log('resolve C'); 
     resolve(); 
    },100); 
    }); 
} 
3

ES7的方式在2017年

<script> 
    var funcs = [ 
    _ => new Promise(resolve => setTimeout(_ => resolve("1"), 1000)), 
    _ => new Promise(resolve => setTimeout(_ => resolve("2"), 1000)), 
    _ => new Promise(resolve => setTimeout(_ => resolve("3"), 1000)), 
    _ => new Promise(resolve => setTimeout(_ => resolve("4"), 1000)), 
    _ => new Promise(resolve => setTimeout(_ => resolve("5"), 1000)), 
    _ => new Promise(resolve => setTimeout(_ => resolve("6"), 1000)), 
    _ => new Promise(resolve => setTimeout(_ => resolve("7"), 1000)) 
    ]; 
    async function runPromisesInSequence(promises) { 
    for (let promise of promises) { 
     console.log(await promise()); 
    } 
    } 
    </script> 
    <button onClick="runPromisesInSequence(funcs)">Do the thing</button> 

This w不順利執行給定的函數(一個接一個),而不是並行執行。參數promises是一組函數,返回Promise

與上面的代碼Plunker例如:http://plnkr.co/edit/UP0rhD?p=preview

0

我寫了這個庫,也許它可以幫助你 https://github.com/LvChengbin/sequence

你可以從NPM安裝:

npm i @lvchengbin/sequence --save 

然後你可以像這樣實施:

Sequence.all([ 
    () => promise1, 
    () => promise2, 
    () => promise3 
]).then(results => { 
    // here you will get all results of the promises in the list 
}).catch(results => { 
    // any of promises failed, and you can get all results of them 
}); 

如果要執行列表中的所有步驟,不管其中是否成功或失敗,你可以使用Sequence.chain

Sequence.all([ 
    () => promise1, 
    () => promise2, 
    () => promise3 
]).then(results => { 
    // here you will get all results of the promises in the list, and you can know which one has succeeded and which one has failed with 
    if(results[ 0 ].status === Sequence.SUCCEEDED) { 
     console.log(results[ 0 ].value); 
    } 
}) 

您還可以創建一個空序列,後來追加的步驟。

const sequence = new Sequence(); 

sequence.append(promise1); 
sequence.append([ promise1, promise2 ]); 
sequence.on('success', (result, index, results) => { 
    // this function will execute for each item succeeded. 
}); 
sequence.on('failed', (result, index, results) => { 
    // this function will execute for each item failed. 
}); 

sequence.on('end',() => { 
    // this function will be executed after all steps in the sequence finished. but you can also append more steps later. 
}); 

如果需要,可以執行這樣的每個步驟指定一個interval

Sequence.all([], 1000); 

Sequence.chain([], 1000); 

Sequence.any([], 1000); 

new Sequence([], { interval : 1000 }); 

所有上面的代碼將執行序列中的各個步驟以1秒的時間間隔。