ошибка при создании обещания bluebird с расширением requirejs для chrome

Я разрабатываю расширение chrome, которое использует chrome.storage.local, и пытаюсь создать обещание из асинхронной функции chrome.storage.local.get(), из которой я хотел бы иметь возможность создавать исключения из а также отклонить/разрешить. Я пытался протестировать это с помощью приведенной ниже реализации, но вижу ошибку, которая из журналов консоли кажется из строки «readLocalStorageObj("prefs").then(function(item) {» (ошибка показана ниже после кода).

require([
    "libs/bluebird"
],
function(Promise) {
    function readLocalStorageObj(itemName) {
        var localReadResult = Promise.method(function(item) {
            console.log("localReadResult():", item);
            // if (chrome.runtime.lastError) {
            //  throw new Error(chrome.runtime.lastError);
            // }

            if (Object.getOwnPropertyNames(item).length > 0) {
                console.log('in if part');
                return item;
            }
            else {
                console.log('in else part');
                // throw new Error("my test exception");
                return undefined;
            }
        });

        chrome.storage.local.get(itemName, localReadResult);
        return localReadResult;
    };

    readLocalStorageObj("prefs").then(function(item) {
        console.log('success', item);
    }, function(e) {
        console.log('fail', e);
    }).error(function(e) {
        console.log('error', e);
    }).catch(ChromeRuntimeError, function(e) {
        console.log('catch', e);
    }).finally(function(a) {
        console.log('finally', a);
    });
});

Ошибка:

Uncaught TypeError: Object function Promise$_method() { var value; switch(arguments.length) { case 0: value = tryCatch1(fn, this, void 0); ломать; случай 1: значение = tryCatch1(fn, this, arguments[0]); ломать; дело......н'

Я не могу понять, в чем причина этого, и был бы очень признателен за любую помощь в этом.

ТИА


person source.rar    schedule 20.03.2014    source источник
comment
Что ты пытаешься сделать? Использование Promise.method в этой ситуации не имеет смысла.   -  person Esailija    schedule 20.03.2014
comment
В основном я пытаюсь получить обещание из API асинхронного чтения chrome.storage. Любые другие предложения о том, как получить обещание в этом случае?   -  person source.rar    schedule 20.03.2014


Ответы (1)


Попробуйте создать обещание следующим образом:

require([
    "libs/bluebird"
],
function(Promise) {
    function readLocalStorageObj(itemName) {
        return new Promise(function(resolve, reject) {
            chrome.storage.local.get(itemName, function(item) {
                if (chrome.runtime.lastError) {
                    return reject(chrome.runtime.lastError);
                }

                if (Object.getOwnPropertyNames(item).length > 0) {
                    return resolve(item);
                }
                reject(new Error("empty item"));
            });
        });
    }

    readLocalStorageObj("prefs").then(function(item) {
        console.log('success', item);
    }, function(e) {
        console.log('fail', e);
    }).error(function(e) {
        console.log('error', e);
    }).catch(ChromeRuntimeError, function(e) {
        console.log('catch', e);
    }).finally(function(a) {
        console.log('finally', a);
    });
});

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

Чтобы избежать шаблонного, вы можете определить общую функцию промисификации для этого соглашения:

function promisfyChrome(fn, ctx) {
    return function() {
        var args = [].slice.call(arguments);
        var self = ctx || this;
        return new Promise(function(resolve, reject) {
            args.push(function(value) {
                if (chrome.runtime.lastError)
                    return reject(chrome.runtime.lastError);
                resolve(value);
            });
            fn.apply(self, args);
        })
    };
}

Применение:

require([
    "libs/bluebird"
],
function(Promise) {
    var chromeLocalStorageGet = promisifyChrome(chrome.storage.local.get, chrome.storage.local);
    function readLocalStorageObj(itemName) {
        return chromeLocalStorageGet(itemName).then(function(item) {
            if (Object.getOwnPropertyNames(item).length > 0) {
                return item;
            }
            throw new Error("empty item");
        });
    }

    readLocalStorageObj("prefs").then(function(item) {
        console.log('success', item);
    }, function(e) {
        console.log('fail', e);
    }).error(function(e) {
        console.log('error', e);
    }).catch(ChromeRuntimeError, function(e) {
        console.log('catch', e);
    }).finally(function(a) {
        console.log('finally', a);
    });
});

Кстати:

    readLocalStorageObj("prefs").then(function(item) {
        console.log('success', item);
    }, function(e) {
        console.log('fail', e);
    }).error(function(e) {
        console.log('error', e);
    }).catch(ChromeRuntimeError, function(e) {
        console.log('catch', e);
    }).finally(function(a) {
        console.log('finally', a);
    });

В этом случае эквивалентно:

    readLocalStorageObj("prefs").then(function(item) {
        console.log('success', item);
    }).catch(Error, function(e) {
        console.log('fail', e);
    }).error(function(e) {
        console.log('error', e);
    }).catch(ChromeRuntimeError, function(e) {
        console.log('catch', e);
    }).finally(function(a) {
        console.log('finally', a);
    });

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

Кроме того, использование .then с двумя функциями очень сложно читать, поэтому это не рекомендуется. Предпочитаю .then(fn).catch(fn2) .then(fn, fn2)

person Esailija    schedule 20.03.2014
comment
Первоначально я использовал что-то похожее на это, однако блок catch() никогда не вызывался (и, как я уже упоминал, я хотел различать исключения и явные сбои). Например, когда я выбрасываю новый TypeError (пустой элемент), он как бы молча терпит неудачу. Кажется, что работают только отказ и решение. Есть идеи, почему? - person source.rar; 20.03.2014
comment
@source.rar .error() совпадает с .catch([ExplicitRejection], function(e){, поэтому он уже будет квалифицироваться как перехват явного вызова reject(chrome.runtime). Если вы не хотите, чтобы это квалифицировалось как неявное, сделайте throw chrome.runtime - person Esailija; 20.03.2014
comment
@source.rar неважно, я только что заметил, что у вас есть универсальный обработчик в первом .then. Это действительно трудно заметить - я предлагаю вам никогда не использовать 2 обработчика одновременно при использовании .then и просто использовать отдельный .catch. В любом случае - обработчик перехвата всех исключений съедает все исключения, поэтому .error() и .catch(ChromeRuntimeError) никогда не запускаются - person Esailija; 20.03.2014
comment
под уловом вы имеете в виду обработчик отклонения? Если да, то разве это не просто обработчик отклонения, а не универсальный? Если я правильно понимаю, универсальным будет уловка (функция (e) {}), связанная с вышеперечисленным? - person source.rar; 20.03.2014
comment
@source.rar да, я также отредактировал свой ответ внизу, надеюсь, это поможет - person Esailija; 20.03.2014
comment
Без обработчика отклонения (как показано в вашем нижнем редактировании). Теперь я получаю возможно необработанную ошибку: undefined on reject(). И я начинаю думать, что API-интерфейсы Chrome не распространяют от них броски. - person source.rar; 20.03.2014
comment
@source.rar ни .error(), ни .catch(ChromeRuntimeError) не квалифицируются как обработчики, когда выбрасывается undefined, поэтому обработчиков нет, и ошибка не обрабатывается, поэтому появляется предупреждение. - person Esailija; 20.03.2014
comment
Да, поэтому в первом случае необходим обработчик отклонения (поскольку другого способа справиться с этим, похоже, нет). И если я заменю reject() броском (именно так я пытался проверить бросок), он никогда не достигнет ни одного из обработчиков. Что, я думаю, означает, что обратный вызов Chrome API, возможно, не распространяет бросок. - person source.rar; 20.03.2014
comment
@source.rar нет, это не так, выброшенное undefined - это ошибка в вашем коде, а не ошибка времени выполнения, с которой вы можете справиться. Вам нужно исправить ошибку, чтобы undefined не выбрасывалось в первую очередь. - person Esailija; 20.03.2014
comment
на самом деле undefined в этом случае не выбрасывается. Я просто делаю reject() без каких-либо аргументов (что фактически не определено), потому что я не хочу возвращать пустой объект, а вызывающая сторона выясняет, что он пустой. - person source.rar; 20.03.2014
comment
@ source.rar Я вижу, вы никогда не должны выдавать не-ошибку, например undefined. Вместо этого вы должны бросить NotFoundError или EmptyItemError или что-то в этом роде. - person Esailija; 20.03.2014
comment
@Esailija promisifyChrome работает как шарм, ответ должен быть принят! - person Marc; 12.12.2014