Возврат значения из обещания: обратный вызов или обещание?

(* Я изменил свой первоначальный вопрос... *)

У меня есть асинхронная функция calculate, которая пересчитывает книгу Excel и печатает, сколько времени это занимает.

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

function calculate1 (mode, fn) {
    return Excel.run(function (ctx) {
        ctx.workbook.application.calculate(mode);
        var before = performance.now();
        return ctx.sync().then(function() {
            var after = performance.now();
            var t = after - before;
            document.getElementById("b").value += 'inside: ' + t + '\n'; 
            fn(t);
        })
    })
}

function calculate2 (mode) {
    return new Promise(function (resolve, reject) {
        return Excel.run(function (ctx) {
            ctx.workbook.application.calculate(mode);
            var before = performance.now();
            return ctx.sync().then(function() {
                var after = performance.now();
                var t = after - before;
                document.getElementById("b").value += 'inside: ' + t + '\n';
                resolve(t); })
            })
    })
}

Вот тест:

function test () {
    var a = [];
    var pm = new OfficeExtension.Promise(function(resolve, reject) { resolve (null); });

    pm
        .then(function() { return calculate1('FullRebuild', function (t) {
            a.push(t); }); })
        .then(function() { return calculate1('FullRebuild', function (t) {
            a.push(t); }); })
        .then(function() { return calculate2('FullRebuild').then(function (result) {
            a.push(result); }); })
        .then(function() { return calculate2('FullRebuild').then(function (result) {
            a.push(result); }); })
        .then(function() {
            document.getElementById("b").value += a.toString() + '\n'; });
}

Я предполагаю, что calculate1 использует callback, а calculate2 использует promise. Может ли кто-нибудь сказать мне, какой путь лучше?

Кроме того, находится ли fn(t) (соответственно, resolve(t)) в нужном месте, или я должен обернуть его другим then?

PS: Excel.run и ctx.sync() — это функции JavaScript. API для офиса; они оба возвращают обещание.


person SoftTimur    schedule 21.06.2016    source источник
comment
Взгляните на этот stackoverflow.com/questions/23803743/. Вы смешиваете обратный вызов и обещания. Я бы предложил вычислить разрешение t, чего вы и хотите.   -  person Deepak Puthraya    schedule 21.06.2016
comment
Мой первоначальный способ обратного вызова не был неправильным (сообщение об ошибке было связано с чем-то другим). Пожалуйста, смотрите мои обновления...   -  person SoftTimur    schedule 21.06.2016
comment
Тот, который больше нравится, тот лучше.   -  person zerkms    schedule 21.06.2016
comment
Я имею в виду, нет ли чего-то лишнего в той или иной мере? Более того, эти два способа принципиально одинаковы или различны?   -  person SoftTimur    schedule 21.06.2016


Ответы (2)


calculate1() представляет собой плохую смесь промисов и обратных вызовов, а calculate2() демонстрирует Антипаттерн явного обещания.

Вот как можно правильно использовать промисы без лишних трат:

function calculate1 (mode) {
    return Excel.run(function (ctx) {
        ctx.workbook.application.calculate(mode);
        var before = performance.now();

        return ctx.sync().then(function() {
            var after = performance.now();
            var t = after - before;
            document.getElementById("b").value += 'inside: ' + t + '\n'; 

            return t;
        });
    });
}

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

return calculate1('FullRebuild')
    .then(function(res) { 
        a.push(res);
        return calculate1('FullRebuild'); 
     })
    .then(function(res) { 
        a.push(res);
        return calculate1('FullRebuild'); 
     })
    .then(function(res) { 
        a.push(res);
        return calculate1('FullRebuild'); 
     })
    .then(function(res) {
        a.push(res);
        document.getElementById("b").value += a.toString() + '\n'; 
    });
person JLRishe    schedule 21.06.2016
comment
Конструктор Promise может быть необходим для переноса Excel.run. Тем не менее, чтобы избежать антипаттерна, он должен выглядеть примерно так: resolve(ctx.sync().then(…)) - person Bergi; 22.06.2016
comment
Код работает... Я вижу, что resolve(t) очень часто используется для передачи значения function (res) {...} в then. Тогда как вы напрямую используете return t, почему это возможно и как это называется? - person SoftTimur; 22.06.2016
comment
@SoftTimur Функция resolve() необходима для создания обещаний из кода, не являющегося обещанием. Поскольку Excel.run() и ctx.sync() основаны на промисах, resolve() здесь не нужно. Причина, по которой это работает, заключается в том, что then() возвращает обещание, которое преобразуется в значение, возвращенное внутри then(). Обещание возвращается внутри then(), обещание, возвращаемое then(), примет это обещание и его разрешение или отклонение. - person JLRishe; 22.06.2016
comment
@Bergi Документация говорит Метод run принимает RequestContext и возвращает обещание (как правило, просто результат ctx.sync()). Хотя он явно не говорит, что его результат является результатом того, что возвращается в его обратном вызове, похоже, что это так, исходя из комментария SoftTimur. - person JLRishe; 22.06.2016

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

Удобство промиса заключается в том, что он позволяет избежать вложенности обратного вызова -> когда для обратного вызова требуется другой обратный вызов... И способ, которым ошибки распространяются на ближайший блок Promise.catch()

function calculate2 (mode) {
    return new Promise(function (resolve, reject) { 

        Excel.run(function (ctx) {       
           ctx.workbook.application.calculate(mode);
           var before = performance.now();

           ctx.sync().then(function() { 
               var after = performance.now(); 
               var t = after - before;  

               resolve(t); 
            })
        })
    })
}

calculate2('FullRebuild')
    .then(function (t) {
         document.getElementById("b").value += 'inside: ' + t + '\n';
         // the return value of a promise resolver is wrapped in a promise too
         // and the value of t will be available as parameter of the following .then(funtion(t)) block
         return t; 
 })
.catch(function(err) {
    // Addresses potential async errors raised since calling calculate2()
 });
person kidroca    schedule 22.06.2016