动态Javascript承诺的模式

时间:2017-08-30 17:26:53

标签: javascript node.js promise

在promise中,我需要在另一个promise之后或者在所述promise之后单独调用它们之后调用并处理不确定数量的异步API响应,但是在另一个之前调用并执行顺序。

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之前处理响应。

我正在努力用promises确定这种模式,我可以动态地创建这些promise,并且只允许在执行和处理不确定和异步之后执行最终的promise。我曾与其他语言合作过,但我很难在Promise中看到它。

感谢任何帮助。

3 个答案:

答案 0 :(得分:0)

我已经更改了以下评论的答案。既然,你提到了ES6的承诺,我会坚持这一点。我们可能会关注两种基本类型的回调。

  1. DOM加载或其他一次性事件回调(window.onload等)
  2. 异步方法回调(AJAX调用,setTimout等)
  3. 从那以后,

    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);
    });
    

    在上述每种情况下,promise(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. 以下评论的链接。

    4. ---老答:请过关---

      我熟悉这个库:https://github.com/kriskowal/q。而且,您可以使用q.allq.allSettled构造执行此操作。也许这就是你要找的东西。

      通常,模式是创建一个返回promise的函数。

      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 ANSWER ---

答案 1 :(得分:0)

首先,如果PromiseA中使用的异步函数不返回promise,则需要 promisify 它们。您可以使用Promise构造函数执行此操作,但使用库(例如bluebird及其promisify方法)会更好。

让我们想象一下,我们有两个函数getUserIdsAsyncgetUserAsync。第一个on返回用户ID列表,getUserAsyncuserId返回用户数据。而且你需要通过他们的ID获取用户列表。 PromiseA的代码看起来如此:

var promiseA = function() {
  return getUserIdsAsync()
    .then(userIds => {
      let ops = users.map(uid => getUserAsync(uid));
      return Promise.all(ops);
    });
}

答案 2 :(得分:0)

以下代码段显示的是不使用任何外部库(如bluebird)的解决方案。它遵循您问题中的代码片段(似乎比需要的更复杂)。

你必须收集数组中的所有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可以提高可读性。也许您应该看看async / await模式以获得更好(可读)的解决方案。

以下是使用async / await的解决方案:

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');
}