2017-08-30 41 views
0

在承諾中,我需要調用並處理不確定數量的異步API響應,然後在另一個承諾內或在承諾之後單獨調用它們,但在另一承諾之前,因此執行順序受到尊重。動態Javascript承諾的模式

var promiseA = function() { 
    return new Promise(function(resolve, reject) { 
    // 1. Establish objects needed from one API endpoint 
    // 2. Call API endpoint for each object and parse 
    // 3. Only then continue to next promise 
    } 
} 

var finalPromise = function() { 
    return new Promise(function(resolve, reject) { 
    // 
    } 
} 

promiseA() 
.then(finalPromise) 

因此,在promiseA中,我找出需要從API中分別輪詢多少個對象。每個請求當然是異步的。我需要進行這些調用並在最終承諾被調用之前處理響應。

我很努力地通過承諾來確定一個模式,我可以動態地創建這些承諾,並且只允許最終承諾在不確定和異步執行和處理後執行。我已經與其他語言一起工作,但我很努力地在Promise中看到它。

任何幫助表示讚賞。

+0

在任何情況下,每一個「呼叫API端點」應該返回承諾本身,'promiseA'不應該使用['Promise' constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-要避免的 - 它)。 – Bergi

+0

目前還不清楚你的代碼應該做什麼。我們不能只給你一個通用的函數promiseA(){return Promise.all(establishEndpointsArr()。map(callEndpoint)); }' – Bergi

回答

0

我已經更改了以下注釋的答案。既然,你提到ES6承諾我會堅持。我們可能關心的回調有兩種基本類型。

  1. DOM負載或其他一次性事件回調(在window.onload等)
  2. 異步方法回調(AJAX調用,setTimout等)

由於,

1 .DOM加載或其他一次性事件

var p = new Promise(function(res, rej) { 
     window.onload = res(); 
};  

2.Plain回調:這些回調是不合作的符合慣例。例如的setTimeout

var p = new Promise(function(res, rej){ 
    setTimeout(function() { 
     //your business/view logic 
     success? res():rej(); //if successful resolve else reject 
    }, 2000); 
}); 

在每種上述情況下的承諾(VAR P)可以纏繞到由函數返回。

var myAsyncMethod = function() {  
    var p = new ... // as mentioned in 1 or 2 
    return p; 
} 

然後使用:

myAsyncMethod() 
.then(function(){/* success-handler */}) 
.catch(function(/* failure-handler */)); 

具體到你的問題,你可能有很多這樣的方法:

function baseAJAXCall (url) { 
    new Promise(functoin(rej, res) { 
     $.get(url, function(err, data){ 
      if(err) { 
       rej(); 
      } 
      else { 
       resolve(data); 
      } 
     }); 
    } 
}; 

function callAPIEndpoint(url) { 
    return baseAJAXCall(url); 
} 

function finalPromiseHandler() { 
    //your final business/view logic 
} 

//USAGE 
callAPIEndpoint('/my-first-call') 
.then(function(data){ 
    var promiseArray = data.map(function(item){ 
     return baseAJAXCall(item.url); 
    }); 
    return Promise.all(promiseArray); 
}) 
.then(finalPromiseHandler) 
.catch(function(){ 
    console.log('.error-message.'); 
}); 

編號:

  1. How do I convert an existing callback API to promises?。從下面的評論

  2. http://www.datchley.name/es6-promises/

  3. 鏈接。

--- OLD答:請忽略---

我熟悉這個庫:https://github.com/kriskowal/q。而且,您可以使用q.allq.allSettled結構來完成此操作。可能是你正在尋找的。

通常情況下,模式是創建一個返回承諾的函數。

function someAsyncFuncName1(url) { 
    var def = q.defer(); 
    //async function 
    $.get(url, function(err, data){ //suppose 
     if(err){ 
      def.reject(); 
     } 
     else { 
      def.resolve(data); //pass the data to the .then() handler. 
     } 
    }); 
    return def.promise; 
} 

function someAsyncFuncName2() { 
    var def = q.defer(); 
    //async function 
    setTimeout(function(){ //suppose 
     //do something 
     if(good) { 
      def.resolve(); 
     } else { 
      def.reject(); 
     } 
    }, 1000); //arbitrary timeout of 1 second 
    return def.promise; 
} 

用法:

q.all([someAsyncFuncName1('/api-1'), someAsyncFuncName2()]) 
.then(function() { 
    //final handler 
}); 

在思想的類似的線,如果你想等待所有承諾返回一個可以使用q.allSettled()

希望這會有所幫助。

--- EOF OLD答案---

+1

避免'someAsyncFuncName1'中的[deferred antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it) ! – Bergi

+1

'q.defer'被認爲是反模式[The Deferred anti-pattern](https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns#the-deferred-anti-pattern)。如果你真的需要創建一個新的Promise,你應該使用常規的'new Promise((resolve,reject)=> {})'模式。 –

+0

謝謝你的評論。我將編輯答案提到這一點。 – 82Tuskers

0

首先,如果PromiseA使用異步函數不返回的承諾,就需要promisify他們。你可以用Promise的構造函數來做到這一點,但是使用類庫比如bluebird和promisify方法好得多。

讓我們想象一下,我們有兩個功能getUserIdsAsyncgetUserAsync。第一個返回用戶ID列表,getUserAsync返回用戶數據userId。你需要通過他們的ID獲得用戶列表。的PromiseA的代碼可能看起來如此:

​​
+0

'。那麼(userIds => {userIds = [1,2,3];'你確定嗎?* SyntaxError:正式參數redeclaration userIds * –

+0

@JaromandaX是的,你說得對,謝謝。 – alexmac

0

下面的片斷顯示,而無需使用任何外部庫像藍鳥的解決方案。它遵循你的問題中的代碼片段(這似乎比需要更復雜)。

您必須收集數組中的所有api promisses。然後你可以打電話Promise.all()獲得所有api promisses結束的承諾。然後你可以做一些最後的東西,比如分析每個承諾的結果,然後繼續。

function getRandomInt(min, max) { 
    return Math.floor(Math.random() * (max - min + 1)) + min; 
} 

var apiEndpoint = function (name) { 
    return new Promise((resolve, reject) => { 
    setTimeout(() => resolve('API ' + name + ' job done'), 1000); 
    }); 
} 

var promiseA = function() { 
    return new Promise((resolve, reject) => { 
    const promisses = []; 
    for (var i=1; i < getRandomInt(3,6); i++) { 
     // 1. Establish objects needed from one API endpoint 
     promisses.push(apiEndpoint('This is number ' + i)); 
    } 
    Promise.all(promisses).then(results => { 
     // do final stuff 
     for (const s of results) { 
     // 2. Call API endpoint for each object and parse 
     console.log(s); 
     } 
     // continue ... 
     // 3. Only then continue to next promise 
     resolve('now it is finished'); 
    }).catch(err => reject(err)); 
    }); 
} 

var finalPromise = function() { 
    return new Promise((resolve, reject) => { 
    console.log('finalPromise'); 
    resolve(); 
    }); 
} 

promiseA() 
    .then(() => finalPromise()) 
    .catch(err => console.log(err)); 

請記住,此解決方案不容易閱讀。使用外部庫或減少promisses可以提高可讀性。也許你應該看看異步/等待模式,以獲得更好(可讀)的解決方案。

這裏是異步/等待下一個解決方案:

function getRandomInt(min, max) { 
    return Math.floor(Math.random() * (max - min + 1)) + min; 
} 

const apiEndpoint = function (name) { 
    return new Promise((resolve, reject) => { 
    setTimeout(() => resolve('API ' + name + ' job done'), 1000); 
    }); 
} 

async function promiseParallel() { 
    const promisses = []; 
    for (let i = 1; i < getRandomInt(3,6); i++) { 
    promisses.push(apiEndpoint('This is number ' + i)); 
    } 
    for (const p of promisses) { 
    const x = await p; 
    console.log(x); 
    } 

    return ('everything is done'); 
} 

promiseParallel().then(result => { 
    console.log(result); 
}).catch(err => console.log(err)); 

如果你想按順序撥打promisses你可以替換爲:

async function promiseSequ() { 
    for (let i = 1; i < getRandomInt(3,6); i++) { 
    const x = await apiEndpoint('This is number ' + i); 
    console.log(x); 
    } 
    return ('everything is done'); 
}