Что означает функция then () в JavaScript?

275

Я видел код, который выглядит так:

myObj.doSome("task").then(function(env) {
    // logic
});

Откуда then()берутся?

Кей Пэйл
источник
8
ОБНОВЛЕНО: я узнал, что это связано с API обещаний CommonJS sitepen.com/blog/2010/01/19/…
Кей Пэйл,

Ответы:

349

Традиционный способ обработки асинхронных вызовов в JavaScript - обратные вызовы. Скажем, нам нужно было сделать три звонка на сервер, один за другим, чтобы настроить наше приложение. С обратными вызовами код может выглядеть примерно так (при условии, что функция xhrGET выполняет вызов сервера):

// Fetch some server configuration
    xhrGET('/api/server-config', function(config) {
        // Fetch the user information, if he's logged in
        xhrGET('/api/' + config.USER_END_POINT, function(user) {
            // Fetch the items for the user
            xhrGET('/api/' + user.id + '/items', function(items) {
                // Actually display the items here
            });
        });
    });

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

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

API Promise был разработан для решения этой проблемы с вложенностью и обработки ошибок.

API Promise предлагает следующее:

  1. Каждая асинхронная задача возвращает promiseобъект.
  2. Каждый promiseобъект будет иметь thenфункцию, которая может принимать два аргумента: success обработчик и errorобработчик.
  3. Успешный или обработчик ошибок в thenфункции будет вызван только один раз , после завершения асинхронной задачи.
  4. thenФункция также будет возвращать promise, чтобы сцепление нескольких вызовов.
  5. Каждый обработчик (успех или ошибка) может вернуть a value, который будет передан следующей функции как a argument, в цепочке promises.
  6. Если обработчик возвращает promise(выполняет другой асинхронный запрос), то следующий обработчик (успех или ошибка) будет вызван только после того, как этот запрос будет завершен.

Таким образом, предыдущий пример кода может перевести что-то вроде следующего, используя обещания и $httpсервис (в AngularJs):

$http.get('/api/server-config').then(
    function(configResponse) {
        return $http.get('/api/' + configResponse.data.USER_END_POINT);
    }
).then(
    function(userResponse) {
        return $http.get('/api/' + userResponse.data.id + '/items');
    }
).then(
    function(itemResponse) {
        // Display items here
    }, 
    function(error) {
        // Common error handling
    }
);

Распространение успеха и ошибки

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

Рассмотрим следующую гипотетическую promiseцепочку с тремя обещаниями, P1, P2 и P3. У каждого promiseесть обработчик успеха и обработчик ошибок, поэтому S1 и E1 для P1, S2 и E2 для P2 и S3 и E3 для P3:

xhrCall()
  .then(S1, E1) //P1
  .then(S2, E2) //P2
  .then(S3, E3) //P3

В нормальном потоке вещей, где нет ошибок, приложение будет проходить через S1, S2 и, наконец, S3. Но в реальной жизни все не так гладко. P1 может столкнуться с ошибкой или P2 может столкнуться с ошибкой, вызвав E1 или E2.

Рассмотрим следующие случаи:

• Мы получили успешный ответ от сервера в P1, но возвращенные данные неверны, или на сервере нет данных (например, пустой массив). В таком случае для следующего обещания P2 он должен вызвать обработчик ошибок E2.

• Мы получаем ошибку для обещания P2, запускающего E2. Но внутри обработчика у нас есть данные из кеша, благодаря чему приложение может загружаться как обычно. В этом случае мы можем захотеть убедиться, что после E2 вызывается S3.

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

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

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

Теперь, что такое отложенный объект?

Отложенные объекты в jQuery представляют собой единицу работы, которая будет выполнена позже, как правило, асинхронно. Как только единица работы завершается, deferredобъект может быть установлен на разрешение или сбой.

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

Отложенные объекты в Jquery: https://api.jquery.com/jquery.deferred/

Отложенные объекты в AngularJs: https://docs.angularjs.org/api/ng/service/ $ q

Sid
источник
3
Очень хорошо написано. Это помогло мне действительно выполнить обещания.
Ju66ernaut
Является ли обработчик ошибок вторым параметром всегда необязательным?
1.21 гигаватт
Это, безусловно, лучший ответ, который я когда-либо видел!
Имам Букс
78

Функция then () связана с «обещаниями JavaScript», которые используются в некоторых библиотеках или инфраструктурах, таких как jQuery или AngularJS.

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

Для получения дополнительной информации см .: http://wildermuth.com/2013/8/3/JavaScript_Promises.

И для угловых обещаний: http://liamkaufman.com/blog/2013/09/09/using-angularjs-promises/

almoraleslopez
источник
4
так это как обратный вызов, который выполняется, когда задача выполнена? Как это отличается
Мухаммед Умер
3
JavaScript Обещание в другом комментарии говорит: A promise can only succeed or fail onceиIf a promise has succeeded or failed and you later add a success/failure callback, the correct callback will be called
Xiao
Кроме того, самородки Promise объясняют, как использовать promiseи что будет делатьcallback
Сяо
На первой странице отсутствуют куски кода (большие белые пробелы). Большинство людей подумают о проверке элемента и найдут URL скрипты внизу. Это сообщение для остальных - скрипки все еще работают;)
DanteTheSmith
1
@MuhammadUmer: прочитайте это stackoverflow.com/a/31453579/1350476 (ответ от Sid)
SharpCoder
32

Насколько мне известно, нет встроенного then()метода javascript(на момент написания этой статьи).

Похоже, что все, что doSome("task")возвращается, имеет метод с именем then.

Если вы зарегистрируете результат возврата doSome()на консоль, вы сможете увидеть свойства того, что было возвращено.

console.log( myObj.doSome("task") ); // Expand the returned object in the
                                     //   console to see its properties.

ОБНОВЛЕНИЕ (Начиная с ECMAScript6) : -

.then()Функция была включена в чистом JavaScript.

Из документации Mozilla здесь ,

Метод then () возвращает Promise. Он принимает два аргумента: функции обратного вызова для случаев успеха и неудачи Обещания.

Объект Promise, в свою очередь, определяется как

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

То есть Promiseдействует как заполнитель для значения, которое еще не вычислено, но должно быть разрешено в будущем. И .then()функция используется, чтобы связать функции, которые будут вызваны в Обещании, когда это будет решено - либо как успех, либо как сбой.

user113716
источник
12
В то время не было встроенного .then, но в ES6 теперь появятся собственные обещания: html5rocks.com/en/tutorials/es6/promises
13
спасибо за этот ответ, я ожидал некоторого крутого обещания обратного вызова, но это оказалось фактической функцией с именем 'then', которая была возвращена.
spartikus
15

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

doit().then(function() { log('Now finally done!') });
log('---- But notice where this ends up!');

// For pedagogical reasons I originally wrote the following doit()-function so that 
// it was clear that it is a promise. That way wasn't really a normal way to do 
// it though, and therefore Slikts edited my answer. I therefore now want to remind 
// you here that the return value of the following function is a promise, because 
// it is an async function (every async function returns a promise). 
async function doit() {
  log('Calling someTimeConsumingThing');
  await someTimeConsumingThing();
  log('Ready with someTimeConsumingThing');
}

function someTimeConsumingThing() {
  return new Promise(function(resolve,reject) {
    setTimeout(resolve, 2000);
  })
}

function log(txt) {
  document.getElementById('msg').innerHTML += txt + '<br>'
}
<div id='msg'></div>

Магнус
источник
5

Вот маленький JS_Fiddle.

затем это стек обратного вызова метода, который доступен после разрешения обещания, он является частью библиотеки, такой как jQuery, но теперь он доступен в собственном JavaScript, а ниже приведено подробное объяснение его работы

Вы можете выполнить Обещание в собственном JavaScript: так же, как есть обещания в jQuery, Каждое обещание может быть сложено, а затем может быть вызвано с помощью обратных вызовов Resolve и Reject. Так вы можете связывать асинхронные вызовы.

Я разветвлялся и редактировал документы MSDN о состоянии зарядки аккумулятора.

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

navigator
    .getBattery()
    .then(function(battery) {
       var charging = battery.charging;
       alert(charging);
    })
    .then(function(){alert("YeoMan : SINGH is King !!");});

Другой пример es6

function fetchAsync (url, timeout, onData, onError) {
    
}
let fetchPromised = (url, timeout) => {
    return new Promise((resolve, reject) => {
        fetchAsync(url, timeout, resolve, reject)
    })
}
Promise.all([
    fetchPromised("http://backend/foo.txt", 500),
    fetchPromised("http://backend/bar.txt", 500),
    fetchPromised("http://backend/baz.txt", 500)
]).then((data) => {
    let [ foo, bar, baz ] = data
    console.log(`success: foo=${foo} bar=${bar} baz=${baz}`)
}, (err) => {
    console.log(`error: ${err}`)
})

Definition :: then - метод, используемый для решения асинхронных обратных вызовов.

это введено в ES6

Пожалуйста, найдите соответствующую документацию здесь Es6 Promises

Тарандип Сингх
источник
Ваш ответ на самом деле не отвечает на вопрос. Это только пример использования API без объяснения того, откуда thenи как он работает. Вы должны улучшить свой ответ, чтобы предоставить эти детали.
Дидье Л
@TarandeepSingh - в первом, затем в операторе, в котором вы сообщаете о состоянии батареи, объект обещания не возвращается. Тогда какая польза от второго тогда
Мохит Джайн
@MohitJain Это демонстрирует, что вы можете сделать несколько обратных вызовов, даже если у вас нет новых обещаний. Поскольку многократные вызовы также могут быть сделаны с Promise.all.
Тарандип Сингх
Что вы подразумеваете под " стеком обратного вызова метода "?
Берги
4

Я подозреваю, что doSome возвращает this, это myObj, который также имеет метод then. Стандартный метод цепочки ...

если doSome не возвращает это, будучи объектом, на котором был выполнен doSome, будьте уверены, что он возвращает некоторый объект с помощью метода then ...

как указывает @patrick, для стандартных js нет then ()

hvgotcodes
источник
1
Я подозреваю, что doSome возвращает это - ничто не подкрепляет / не оправдывает такие подозрения
Genèse
2

.then возвращает обещание в асинхронной функции.

Хорошим примером будет:

var doSome = new Promise(function(resolve, reject){
    resolve('I am doing something');
});

doSome.then(function(value){
    console.log(value);
});

Чтобы добавить к нему другую логику, вы также можете добавить reject('I am the rejected param')вызов функции и console.log.

appdesigns
источник
1

doSome ("задача") должна возвращать объект обещания, и это обещание всегда имеет функцию then. Так что ваш код такой же, как этот

promise.then(function(env) {
    // logic
}); 

и вы знаете, что это просто обычный вызов функции-члена.

Hardeep SINGH
источник
0

В данном случае then()это метод класса объекта, возвращаемого doSome()методом.


источник
0

Функция ".then ()" широко используется для обещанных объектов в программировании Asynchoronus для приложений Магазина Windows 8. Насколько я понял, это работает как обратный вызов.

Найти подробности в этом документе http://msdn.microsoft.com/en-us/library/windows/apps/hh700330.aspx

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

user2508620
источник
0

Другой пример:

new Promise(function(ok) {
   ok( 
      /* myFunc1(param1, param2, ..) */
   )
}).then(function(){
     /* myFunc1 succeed */
     /* Launch something else */
     /* console.log(whateverparam1) */
     /* myFunc2(whateverparam1, otherparam, ..) */
}).then(function(){
     /* myFunc2 succeed */
     /* Launch something else */
     /* myFunc3(whatever38, ..) */
})

Та же логика с использованием стрелочных функций:

new Promise((ok) =>
   ok( 
      /* myFunc1(param1, param2, ..) */
)).then(() =>
     /* myFunc1 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc2(whateverparam1, otherparam, ..) */
).then(() =>
     /* myFunc2 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc3(whatever38, ..) */
)

NVRM
источник
-4

Я опоздал примерно на 8 лет, ну ... в любом случае, я действительно не знаю, что тогда делает (), но, возможно, у MDN может быть ответ. На самом деле, я мог бы понять это немного больше.

Это покажет вам всю информацию (надеюсь), вам нужно. Если кто-то уже разместил эту ссылку. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

Формат - promise.prototype.then (). Обещание и прототип в некотором роде похожи на переменные, но не похожи на переменные в javascript. Я имею в виду, как и другие вещи, такие как navigator.getBattery (). Then (), где это на самом деле существует, но он едва используется в Интернете, показывает статусы батареи устройства, дополнительную информацию и дополнительную информацию о MDN, если вам интересно.

Джонатан Дж. Пекани
источник