Что такое асинхронный код, основанный на обещаниях, обеспечивает, что асинхронный код на основе обратного вызова не работает? [Дубликат]

Чтобы отредактировать код и исправить ошибку:

while True:
    try:
       age = int(input("Please enter your age: "))
       if age >= 18: 
           print("You are able to vote in the United States!")
           break
       else:
           print("You are not able to vote in the United States.")
           break
    except ValueError:
       print("Please enter a valid response")
347
задан Benjamin Gruenbaum 20 March 2014 в 18:55
поделиться

7 ответов

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

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

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

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

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 звонки), но вам нужно было подождать три? Без обещаний вам нужно создать какой-то счетчик. С обещаниями, использующими нотацию 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.
});

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

521
ответ дан Scott Arciszewski 22 August 2018 в 04:10
поделиться
  • 1
    Они действительно не должны были называть это «обещанием». & Quot; Будущее & Quot; по крайней мере в 100 раз лучше. – Pacerier 2 May 2014 в 19:47
  • 2
    @Pacerier, потому что будущее не было испорчено jQuery? – Esailija 31 January 2015 в 13:01
  • 3
    Альтернативный шаблон (в зависимости от желаемого: api (), затем (api2). Then (api3). Then (doWork), то есть если функции api2 / api3 принимают входные данные с последнего шага и возвращают сами новые обещания, они могут быть просто прикованы без дополнительной упаковки. То есть они сочиняют. – Dtipson 22 December 2015 в 20:11
  • 4
    Что делать, если в api2 и api3 есть асинхронные операции? будет ли последний .then вызываться только после завершения этих асинхронных операций? – NiCk Newman 24 January 2016 в 20:45
  • 5
    Почему вы пометили меня? Я немного исправил грамматику. Я не эксперт JS. :) – Scott Arciszewski 29 April 2016 в 15:37

Никакие обещания не являются просто оболочкой для обратных вызовов

. Вы можете использовать собственные обещания javascript с узлом 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
2
ответ дан Apoorv 22 August 2018 в 04:10
поделиться

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

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

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

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

2. Инверсия управления

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

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

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

4
ответ дан Benjamin Gruenbaum 22 August 2018 в 04:10
поделиться

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

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

Итак, в чем главная идея?

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

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

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

Обещания связаны с цепью ( monadic , если хотите):

  • Если вам нужно преобразовать значение, представляемое обещанием, вы map преобразуете функцию над обещанием и получите новое обещание, которое представляет преобразованный результат. Вы не можете синхронно получить значение, чтобы использовать его каким-то образом, но вы можете легко снять преобразование в контексте обещания. Нет обратных вызовов шаблонов.
  • Если вы хотите связать две асинхронные задачи, вы можете использовать метод .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 */);

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

Что представляет собой большая проблема с обещаниями?

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

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

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

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

146
ответ дан Community 22 August 2018 в 04:10
поделиться
  • 1
    привет Берги, у вас было бы что-нибудь интересное, чтобы добавить к этому вопросу? [Д0] stackoverflow.com/questions/22724883/… – Sebastien Lorber 1 April 2014 в 15:38
  • 2
    @Sebastien: Я мало знаю о Scala (пока), и я мог только повторить то, что сказал Бенджамин :-) – Bergi 1 April 2014 в 15:57
  • 3
    Просто небольшое замечание: вы не можете использовать .then(console.log), поскольку console.log зависит от контекста консоли. Таким образом, это приведет к ошибке незаконного вызова. Используйте console.log.bind(console) или x => console.log(x) для привязки контекста. – Tamas Hegedus 20 November 2015 в 12:07
  • 4
    @hege_hegedus: Есть среды, где console методы уже связаны. И, конечно же, я только сказал, что оба гнезда имеют точно такое же поведение, а не то, что любой из них будет работать: -P – Bergi 20 November 2015 в 12:46
  • 5
    Это было замечательно. Это то, что мне нужно: меньше кода и больше интерпретации. Спасибо. – Adam Patterson 5 March 2017 в 21:58

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

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

// Parallel:
Promise.all([
    api1(),
    api2(),
    api3()
]).then(([r1, r2, r3]) => {
    // Done
});
10
ответ дан Duncan Luk 22 August 2018 в 04:10
поделиться

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

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

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

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

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

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

    return api3Result;
}
15
ответ дан John Weisz 22 August 2018 в 04:10
поделиться
  • 1
    – user3344977 15 June 2017 в 21:18
  • 2
    – Michael McGinnis 31 October 2017 в 09:13
  • 3
    Если вы не используете аргументы в методах apiX, вы также можете пропустить все функции стрелок: api().then(api2).then(api3).then(r3 => console.log(r3)). – oligofren 7 November 2017 в 19:55
  • 4
    @MichaelMcGinnis - Благотворное влияние обещаний на скучный аддон обратного вызова - это как взрывающаяся сверхновая в темном углу пространства. – John Weisz 24 May 2018 в 08:19

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

4
ответ дан Kjell Schubert 22 August 2018 в 04:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: