Подтвердить что ты не робот

Как вы правильно возвращаете несколько значений из обещания?

Я несколько раз сталкивался с определенной ситуацией, и я не знал, как правильно ее решить. Предположим, что следующий код:

somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( amazingData ) {
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
}

Теперь может возникнуть ситуация, когда я хочу иметь доступ к amazingData в afterSomethingElse.

Одним из очевидных решений было бы вернуть массив или хэш из afterSomething, потому что, вы можете вернуть только одно значение из функции. Но мне интересно, есть ли способ afterSomethingElse принять 2 параметра и вызвать его аналогичным образом, поскольку это намного легче документировать и понимать.

Мне просто интересно об этой возможности, так как есть Q.spread, что делает что-то похожее на то, что я хочу.

4b9b3361

Ответ 1

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

Обещание неотъемлемо разрешается с одним значением - это часть того, как Q работает, как работает Promises/A + и как работает абстракция.

Ближе всего вы можете использовать Q.spread и возвращать массивы или использовать деструктурирование ES6, если оно поддерживается, или вы хотите использовать инструмент транспиляции, такой как BabelJS.

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

Ответ 2

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

Другая стратегия заключается в том, чтобы сохранить значение через закрытие, а не передавать его:

somethingAsync().then(afterSomething);

function afterSomething(amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

Полностью, а не частично вложенная форма (эквивалентная, возможно, более согласованная):

somethingAsync().then(function (amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

Ответ 3

вы можете передать только одно значение, но оно может быть массивом с краткими значениями внутри, например:

function step1(){
  let server = "myserver.com";
  let data = "so much data, very impresive";
  return Promise.resolve([server, data]);
}

с другой стороны, вы можете использовать выражение destructuring для ES2015, чтобы получить отдельные значения.

function step2([server, data]){
  console.log(server); // print "myserver.com"
  console.log(data);   // print "so much data, very impresive"
  return Promise.resolve("done");
}

чтобы назвать оба обещания, связав их:

step1()
.then(step2)
.then((msg)=>{
  console.log(msg); // print "done"
})

Ответ 4

Две вещи, которые вы можете сделать, вернуть объект

somethingAsync()
    .then( afterSomething )
    .then( afterSomethingElse );

function processAsync (amazingData) {
     //processSomething
     return {
         amazingData: amazingData, 
         processedData: processedData
     };
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}

function afterSomethingElse( dataObj ) {
    let amazingData = dataObj.amazingData,
        processedData = dataObj.proccessedData;
}

Используйте область действия!

var amazingData;
somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( returnedAmazingData ) {
  amazingData = returnedAmazingData;
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
  //use amazingData here
}

Ответ 5

Вот как я считаю, вы должны делать.

расщепление цепочки

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

Как ваш пример запускает некоторый код, я предполагаю, что он все объявлен внутри функции. Я назову это toto(). Тогда у нас будет другая функция, которая будет запускаться как afterSomething(), так и afterSomethingElse().

function toto() {
    return somethingAsync()
        .then( tata );
}

Вы также заметите, что я добавил оператор return, поскольку обычно это путь к Promises - вы всегда возвращаете обещание, чтобы мы могли поддерживать цепочку, если это необходимо. Здесь somethingAsync() создаст amazingData, и он будет доступен везде внутри новой функции.

Теперь, как будет выглядеть эта новая функция, как правило, зависит от того, асинхронный процесс processAsync()?

processAsync не асинхронный

Нет причин для чрезмерного сжатия вещей, если processAsync() не является асинхронным. Какой-то старый хороший последовательный код сделает это.

function tata( amazingData ) {
    var processed = afterSomething( amazingData );
    return afterSomethingElse( amazingData, processed );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

Обратите внимание, что не имеет значения, выполняет ли afterSomethingElse() что-то асинхронное или нет. Если это произойдет, обещание будет возвращено, и цепочка может продолжаться. Если это не так, то возвращается значение результата. Но поскольку функция вызывается из then(), значение будет в любом случае завернуто в обещание (по крайней мере, в необработанном Javascript).

processAsync асинхронный

Если процессAsync() является асинхронным, код будет выглядеть несколько иначе. Здесь мы рассмотрим afterSomething() и afterSomethingElse(), которые больше не будут использоваться в другом месте.

function tata( amazingData ) {
    return afterSomething()
        .then( afterSomethingElse );

    function afterSomething( /* no args */ ) {
        return processAsync( amazingData );
    }
    function afterSomethingElse( processedData ) {
        /* amazingData can be accessed here */
    }
}

То же, что и раньше для afterSomethingElse(). Он может быть асинхронным или нет. Обещание будет возвращено или значение будет завершено в обещанное обещание.


Ваш стиль кодирования довольно близок к тому, что я использую, поэтому я ответил даже спустя 2 года. Я не большой поклонник анонимных функций во всем мире. Мне трудно читать. Даже если это довольно распространено в сообществе. Это, как мы заменили обратный вызов - ад с обещанием чистилище.

Мне также нравится сохранять имя функций в коротком коротком. В любом случае они будут определяться локально. И большую часть времени они будут вызывать другую функцию, определенную в другом месте - так многократно используемую - для выполнения задания. Я даже делаю это для функций только с одним параметром, поэтому мне не нужно вводить и выключать функцию, когда я добавляю/удаляю параметр в подпись функции.

Пример использования

Вот пример:

function goingThroughTheEatingProcess(plenty, of, args, to, match, real, life) {
    return iAmAsync()
        .then(chew)
        .then(swallow);

        function chew(result) {
            return carefullyChewThis(plenty, of, args, "water", "piece of tooth", result);
        }

        function swallow(wine) {
            return nowIsTimeToSwallow(match, real, life, wine);
        }
}

function iAmAsync() {
    return Promise.resolve("mooooore");
}

function carefullyChewThis(plenty, of, args, and, some, more) {
    return true;
}

function nowIsTimeToSwallow(match, real, life, bobool) {
}

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

Недостатком этой методики является то, что она определяет множество функций. Но это болит, я боюсь, чтобы избежать анонимных функций повсюду. И каков же риск: переполнение стека? (Шутка!)


Использование массивов или объектов, как определено в других ответах, тоже будет работать. Это в некотором роде ответ предложенный Кевином Ридом.

Вы также можете использовать bind() или Promise.all(). Обратите внимание, что они все равно потребуют от вас разделить ваш код.

используя bind

Если вы хотите, чтобы ваши функции были повторно использованы, но на самом деле не нужно сохранять то, что находится внутри очень короткого, вы можете использовать bind().

function tata( amazingData ) {
    return afterSomething( amazingData )
        .then( afterSomethingElse.bind(null, amazingData) );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

Чтобы упростить его, bind() добавит список аргументов (кроме первого) в функцию, когда она вызывается.

используя Promise.all

В своем сообщении вы упомянули использование spread(). Я никогда не использовал фреймворк, который вы используете, но вот как вы должны его использовать.

Некоторые считают, что Promise.all() является решением всех проблем, поэтому заслуживает упоминания, я думаю.

function tata( amazingData ) {
    return Promise.all( [ amazingData, afterSomething( amazingData ) ] )
        .then( afterSomethingElse );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( args ) {
    var amazingData = args[0];
    var processedData = args[1];
}

Вы можете передать данные в Promise.all() - обратите внимание на наличие массива - до тех пор, пока promises, но убедитесь, что ни один из Promises не работает, иначе он перестанет обрабатываться.

И вместо того, чтобы определять новые переменные из аргумента args, вы должны иметь возможность использовать spread() вместо then() для всякого рода удивительной работы.

Ответ 6

Просто создайте объект и извлеките аргументы из этого объекта.

let checkIfNumbersAddToTen = function (a, b) {
return new Promise(function (resolve, reject) {
 let c = parseInt(a)+parseInt(b);
 let promiseResolution = {
     c:c,
     d : c+c,
     x : 'RandomString'
 };
 if(c===10){
     resolve(promiseResolution);
 }else {
     reject('Not 10');
 }
});
};

Извлеките аргументы из promResolution.

checkIfNumbersAddToTen(5,5).then(function (arguments) {
console.log('c:'+arguments.c);
console.log('d:'+arguments.d);
console.log('x:'+arguments.x);
},function (failure) {
console.log(failure);
});

Ответ 7

Все, что вы вернете из обещания, будет завершено в обещание, которое будет развернуто на следующей стадии .then().

Это становится интересным, когда вам нужно вернуть одно или несколько обещаний вместе с одним или несколькими синхронными значениями, такими как;

Promise.resolve([Promise.resolve(1), Promise.resolve(2), 3, 4])
       .then(([p1,p2,n1,n2]) => /* p1 and p2 are still promises */);

В этих случаях было бы важно использовать Promise.all() для получения обещаний p1 и p2 развернутых на следующем этапе .then() таком как

Promise.resolve(Promise.all([Promise.resolve(1), Promise.resolve(2), 3, 4]))
       .then(([p1,p2,n1,n2]) => /* p1 is 1, p2 is 2, n1 is 3 and n2 is 4 */);

Ответ 8

Вы можете проверить Observable, представленный Rxjs, позволяет вернуть более одного значения.