Не являются ли promises только обратные вызовы?

Я разрабатываю JavaScript в течение нескольких лет, и я вообще не понимаю суеты о promises.

Кажется, что все, что я делаю, это изменение:

api(function(result){
    api2(function(result2){
        api3(function(result3){
             // do work
        });
    });
});

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

api().then(function(result){
     api2().then(function(result2){
          api3().then(function(result3){
               // do work
          });
     });
});

Что больше кода и менее читаемо. Я ничего здесь не получил, это не неожиданно волшебным образом "плоское". Не говоря уже о том, чтобы преобразовать вещи в promises.

Итак, какова большая суета о promises здесь?

Ответ 1

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

api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
});

Конечно, не намного меньше кода, но гораздо более читабельным.

Но это еще не конец. Откройте для себя истинные преимущества: что, если вы хотите проверить какую-либо ошибку на любом из шагов? Было бы ад, чтобы сделать это с помощью обратных вызовов, но с promises, является частью торта:

api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
}).catch(function(error) {
     //handle any error that may occur before this point
});

Совсем аналогично блоку try { ... } catch.

Еще лучше:

api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
}).catch(function(error) {
     //handle any error that may occur before this point
}).then(function() {
     //do something whether there was an error or not
     //like hiding an spinner if you were performing an AJAX request.
});

И еще лучше: что делать, если эти 3 вызова api, api2, api3 могут выполняться одновременно (например, если они были вызовами AJAX), но вам нужно было дождаться трех? Без promises вам нужно создать какой-то счетчик. С помощью promises, используя нотацию ES6, это еще один кусок торта и довольно аккуратный:

Promise.all([api(), api2(), api3()]).then(function(result) {
    //do work. result is an array contains the values of the three fulfilled promises.
}).catch(function(error) {
    //handle the error. At least one of the promises rejected.
});

Надеюсь, вы увидите Promises в новом свете.

Ответ 2

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

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

Так в чем основная идея?

Обещания - это объекты, представляющие результат одного (асинхронного) вычисления. Они разрешают этот результат только один раз. Есть несколько вещей, что это значит:

Обещания реализуют шаблон наблюдателя:

  • Вам не нужно знать обратные вызовы, которые будут использовать значение до завершения задачи.
  • Вместо ожидания обратных вызовов в качестве аргументов для ваших функций, вы можете легко return объект Promise
  • В обещании будет сохранено значение, и вы сможете прозрачно добавить обратный вызов, когда захотите. Он будет вызван, когда будет доступен результат. "Прозрачность" подразумевает, что когда у вас есть обещание и вы добавляете к нему обратный вызов, для вашего кода не имеет значения, достигнут ли результат - API и контракты одинаковы, что значительно упрощает кэширование/запоминание.
  • Вы можете легко добавить несколько обратных вызовов

Обещания являются связными (монадические, если хотите):

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

Звучит сложно? Время для примера кода.

var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
    var p2 = api2(); // returning a promise
    return p2; // The result of p2 …
}); // … becomes the result of p3

// So it does not make a difference whether you write
api1().then(function(api1Result) {
    return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
    return api2();
}).then(console.log)

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

api1().then(api2).then(api3).then(/* do-work-callback */);

Если видение кода этих методов помогает понять, здесь приведена самая простая библиотека обещаний в несколько строк.

Что за суета по поводу обещаний?

Абстракция Promise позволяет намного лучше комбинировать функции. Например, рядом с цепочкой then функция all создает обещание для комбинированного результата нескольких обещаний параллельного ожидания.

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

Не говоря уже о необходимости превращать вещи в обещания.

Это довольно тривиально на самом деле с хорошими библиотеками обещаний, см. Как преобразовать существующий API обратного вызова в обещания?

Ответ 3

В дополнение к уже установленным ответам, с помощью стрелок ES6 функции Promises превращаются из скромно сияющего маленького синего карлика прямо в красного гиганта. Это вот-вот рухнет в сверхновую

api().then(result => api2()).then(result2 => api3()).then(result3 => console.log(result3))

Как отметил олигофрен, без аргументов между вызовами API вам не нужны анонимные функции-оболочки:

api().then(api2).then(api3).then(r3 => console.log(r3))

И, наконец, если вы хотите достичь сверхмассивного уровня черной дыры, можно ожидать Promises:

async function callApis() {
    let api1Result = await api();
    let api2Result = await api2(api1Result);
    let api3Result = await api3(api2Result);

    return api3Result;
}

Ответ 4

В дополнение к другим ответам синтаксис ES2015 легко смешивается с promises, сокращая еще более шаблонный код:

// Sequentially:
api1()
  .then(r1 => api2(r1))
  .then(r2 => api3(r2))
  .then(r3 => {
      // Done
  });

// Parallel:
Promise.all([
    api1(),
    api2(),
    api3()
]).then(([r1, r2, r3]) => {
    // Done
});

Ответ 5

В дополнение к удивительным ответам выше, можно добавить еще 2 пункта:

1. Семантическая разница:

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

И наоборот, обратные вызовы обрабатывают события. Таким образом, если интересующее вас событие произошло до того, как обратный вызов был зарегистрирован, обратный вызов не вызывается.

2. Инверсия контроля

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

См. Цикл событий Javascript для объяснения.

В Promises управление находится в вызывающей программе. Метод .then() может быть вызван в любое время, если мы сохраним объект обещания.

Ответ 6

Promises не являются обратными вызовами, оба являются идиомами программирования, которые облегчают асинхронное программирование. Использование асинхронного/ожидающего стиля программирования с помощью сопрограмм или генераторов, которые возвращают promises, можно считать третьей такой идиомой. Сравнение этих идиом на разных языках программирования (включая Javascript) можно найти здесь: https://github.com/KjellSchubert/promise-future-task

Ответ 7

Нет promises - это просто оболочка при обратных вызовах

Пример   Вы можете использовать javascript native promises с node js

my cloud 9 code link : https://ide.c9.io/adx2803/native-promises-in-node

/**
* Created by dixit-lab on 20/6/16.
*/

var express = require('express');
var request = require('request');   //Simplified HTTP request client.


var app = express();

function promisify(url) {
    return new Promise(function (resolve, reject) {
    request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
        resolve(body);
    }
    else {
        reject(error);
    }
    })
    });
}

//get all the albums of a user who have posted post 100
app.get('/listAlbums', function (req, res) {
//get the post with post id 100
promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) {
var obj = JSON.parse(result);
return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums')
})
.catch(function (e) {
    console.log(e);
})
.then(function (result) {
    res.end(result);
}
)

})


var server = app.listen(8081, function () {

var host = server.address().address
var port = server.address().port

console.log("Example app listening at http://%s:%s", host, port)

})


//run webservice on browser : http://localhost:8081/listAlbums

Ответ 8

Нет, совсем нет.

Обратные вызовы - это просто функции в JavaScript, которые должны вызываться и затем выполняться после завершения выполнения другой функции. Так как это происходит?

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

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

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