ES6 Dyanmic Promise Цепочка из массива

1

сценарий

У меня есть массив URL-адресов, которые мне нужно загрузить, однако каждый из них также должен быть снабжен уникальным идентификатором транзакции, который должен быть запрошен с сервера и только увеличивается при успешном выполнении запроса.

проблема

Когда я прохожу через массив, мне нужно дождаться как запроса идентификатора транзакции, так и запроса на выполнение файла до начала следующей итерации цикла, но количество файлов не фиксировано, поэтому необходимо динамически построить цепочку обещания.

ПСЕВДОКОД

Ниже приведен псевдокод, getFiles() - проблема, потому что все запросы получают один и тот же идентификатор транзакции, поскольку они не дождались завершения предыдущего запроса.

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

Вопрос

Как объединить цепочные обещания вместе динамически?

Ответ

Здесь JSFiddle ответа Овидиу

Теги:
ecmascript-6
promise
es6-promise

4 ответа

5
Лучший ответ

Функциональный подход состоит в том, чтобы использовать reduce для повторения и возвращать окончательное обещание, прикованное по каждому подзадачу. Это также помогает создавать результаты, например, в массиве:

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 и использует id для getFile
  • как только файл будет извлечен, он вернет массив, содержащий curFiles установленный в curPromise (предыдущие значения) и объединит в него новый файл
  • конечный результат будет единым обещанием со всеми собранными файлами
  • 0
    Действительно хороший ответ, [...a, b] это новый синтаксис для меня, и это прекрасно. Благодарю.
0

Попробуйте использовать async/wait.

Подробнее здесь

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)
}
}
  • 3
    ES6 aka ES2015 НЕ включает асинхронное / ожидание. эта функция только официально готова в ES2017. nodejs официально поддерживает это с версии 7.6
0

Вы можете сделать что-то в этом направлении

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

Вы можете упростить логику, если вы запускаете ее через синхронный исполнитель nsynjs. Nsynjs приостанавливается, когда какая-то функция оценивает обещание, а затем присваивает результат свойству 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);
    };
};

Ещё вопросы

Сообщество Overcoder
Наверх
Меню