【问题标题】:ES6 Dyanmic Promise Chaining from array来自数组的 ES6 动态承诺链
【发布时间】:2017-11-07 03:40:00
【问题描述】:

场景

我有一组需要下载的 URL,但是还必须为每个 URL 提供一个唯一的事务 ID,该事务 ID 必须从服务器请求,并且仅在请求成功时递增。

问题

当我循环遍历数组时,我需要等待事务 ID 请求和文件请求完成,然后再开始循环的下一次迭代,但文件数不固定,因此需要动态构建一连串的承诺。

伪代码

下面是一些伪代码,getFiles() 是问题所在,因为所有请求都获得相同的事务 ID,因为它们不等待前一个请求完成。

function getTransationId(){
    return new Promise((resolve,reject)=> {
        let id = getNextTransactionId();
        if(id!=error){
            resolve(id);
        }else{
            reject(error);
        }
    })
}

function getFile(url, transactionId){
    return new Promise((resolve,reject)=>{
        http.request(url+transactionId, function(err,response){
            if(err){
                reject(err);
            }else{
                resolve(response);
            }
        });
    });
}

function getFilesFromArray(urlArray){
    for(let url of urlArray){
        getTransactionId().then(resolve=>getFile(url,resolve),reject=>console.error(reject));
    }
}

问题

如何动态地将 Promise 链接在一起?

回答

这是JSFiddleOvidiu's 答案

【问题讨论】:

  • 如果getNextTransactionId 返回id,为什么需要将它包装在Promise 中?然后,您的示例将与未定义的error 进行比较。如果不知道 getNextTransactionId 返回的是什么,很难判断是什么导致了问题。
  • getTransactionId() 是对服务器的调用,因此我们需要在继续之前等待响应,否则 Id 将是 undefined。就错误而言,我已经过度简化了说明问题的过程,因此调用是伪代码。

标签: javascript node.js ecmascript-6 promise es6-promise


【解决方案1】:

一种函数式方法是使用reduce 迭代并返回从每个子promise 链接起来的最终promise。它还有助于构建结果,例如在数组中:

function getFilesFromArray(urlArray){
    const filesPromise = urlArray.reduce((curPromise, url) => {
        return curPromise
           .then(curFiles => {
                return getTransactionId()
                    .then(id => getFile(url, id))
                    .then(newFile => [...curFiles, newFile]);
           });
    }, Promise.resolve([]));

    filesPromise.then(files => {
         console.log(files);
    }
}

这有效地构建了一个承诺链:

  • 以静态Promise 开头,值[] 表示初始文件集:Promise.resolve([])
  • 在每次迭代中,返回一个等待链中 curPromise 的承诺,然后
  • 执行getTransactionId 并使用getFile 的id
  • 一旦文件被检索到,它将返回一个数组,其中包含在curPromise 中设置的curFiles(以前的值),并将newFile 连接到其中
  • 最终结果将是一个包含所有文件的单一承诺

【讨论】:

  • 非常好的答案,[...a, b] 对我来说是新语法,而且很漂亮。谢谢。
【解决方案2】:

你可以按照这些思路做一些事情

function getAllFiles(i, results, urlArray) {
    if(i == urlArray.length) return;

    getTransationId().then(id => {
        return new Promise((resolve, reject) => {
            http.request(urlArray[i] + id, (err, response) => {
                if(err){
                    reject();
                }else{
                    results.push(response);
                    resolve();
                }
            });
        });
    }).then(() => {
        getAllFiles(i + 1, results, urlArray);
    })
}

【讨论】:

    【解决方案3】:

    尝试使用异步/等待。

    阅读更多here

    async function getFilesFromArray(urlArray) {
          for(let url of urlArray){
             //wrap this in a try/catch block if you want to continue with execution 
             //if you receive an error from one of the functions
             const transactionId =await getTransactionId()
             const file = await getFile(url,transactionId)
    }
    }
    

    【讨论】:

    • ES6 aka ES2015 NOT 包含异步/等待。此功能仅在 ES2017 中正式准备就绪。 nodejs 从 7.6 版本开始正式支持此功能
    【解决方案4】:

    如果您通过同步执行器nsynjs 运行它,您可以简化逻辑。 Nsynjs 会在某个函数的计算结果为 promise 时暂停,然后将结果分配给 data 属性。代码会变成这样:

    function getFilesFromArray(urlArray){
        for(var i = 0; i<urlArray.length; i++) {
            var trId = getTransactionId().data;
            // trId is ready here
            var fileContent = getFile(urlArray[i],trId).data;
            // file data is ready here
            console.log('fileContent=',fileContent);
        };
    };
    
    nsynjs.run(getFilesFromArray,{},urls,function(){
        console.log('getFilesFromArray is done');
    });
    

    getFilesFromArray 可以进一步简化为:

    function getFilesFromArray(urlArray){
        for(var i = 0; i<urlArray.length; i++) {
            var fileContent = getFile(urlArray[i],getTransactionId().data).data;
            console.log('fileContent=',fileContent);
        };
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-13
      • 2018-03-19
      • 2017-02-05
      • 2016-04-02
      • 2017-07-02
      • 2016-08-06
      • 1970-01-01
      相关资源
      最近更新 更多