Автор оригинала: FreeCodeCamp Community Member.
Андреа Куутифарис
Тест стоит тысячи слов … Или это было картина …?
Я думаю, что лучший способ объяснить обещания JavaScript через примеры. Что такое хорошее, самостоятельно, а также короткий способ написать пример? Тест!
Для тех, кто никогда не видел жасминовый тестовый костюм, Это ('...', (сделано) => {..
.}) – тест A ND D
Одной – это функция, которая должна быть выполнена, когда асинхронный тест завершен.
Правила здесь:
- Каждый тест начинается, утверждая что-то на английском языке Отказ Вы должны вывести, почему тестовый код подразумевает, что утверждение теста верно.
- Некоторые тесты имеют ожидания. Если испытание проходит, ожидания верны.
- Другие тесты полагаются на обратный вызов
сделано ()
быть названным. Еслисделано ()
не вызывается, тест терпит неудачу.
Каждый тест в Это jsfiddle Так что не стесняйтесь играть с ним во время чтения. Особенно, если у вас есть некоторые сомнения относительно любого из тестов, измените тестовый код и изучите то, что происходит.
Тесты
Начнем с основы обещания:
it('Promise executor is run SYNCHRONOUSLY', () => { let executorRun = false; new Promise(function executor() { executorRun = true; }); expect(executorRun).toBe(true);});it('you can resolve a promise', (done) => { new Promise((resolve) => setTimeout(resolve, 1)) .then(done);});it('... or you can reject a promise', (done) => { new Promise((resolve, reject) => setTimeout(reject, 1)) .then(undefined, done);});it('An error inside the executor, rejects the promise', (done) => { new Promise(function executor() { throw 'Error'; }).catch(done);});
Кажется, когда вы звоните разрешать ()
первый тогда (...)
Обратный вызов запущен. Если вы звоните Отклонить ()
или ошибка брошен, поймать ()
или второй обратный вызов тогда (...)
бежит
Также Исполнитель обещания работает синхронно Отказ Это означает, что обещания – это способ обрабатывать асинхронный код, а не выполнять задачи в асинхронных нитках. Использовать Веб-работники Если вы хотите выполнить какой-то код JavaScript за пределами основного потока.
Давайте узнаем подробнее, что те тогда (...)
и поймать ()
Функции и какие «цепочки обещания» означает:
// Chaining promises
it('you can chain promise because .then(...) returns a promise' , (done) => { fetch('https://jsonplaceholder.typicode.com/posts/1') .then(response => response.json()) .then(json => expect(json.userId).toBe(1)) .then(done);});it('you can use the fail callback of .then(success, fail) to ' + 'handle rejected promises', (done) => { Promise.reject() .then(function success() { throw 'I must not be executed'; }, function fail() { done(); });});it('... or you can use .catch() to handle rejected promises' , (done) => { Promise.reject() .then(function success() { throw 'I must not be executed'; }) .catch(done);});it('also .catch() returns a promise, allowing promise chaining' , (done) => { Promise.reject() .catch(() => undefined) .then(done);});it('you must return a rejected promise if you want to ' + 'execute the next fail callback', (done) => { function someApiCall() { return Promise.reject('Error'); } someApiCall() .catch((err) => { console.error(err); // Without the line below, .catch gets not called return Promise.reject(err); }) .catch(done);});it('... or you can throw an error if you want to ' + 'execute the next fail callback', (done) => { function someApiCall() { return Promise.reject('Error'); } someApiCall() .catch((err) => { console.error(err); throw err; // Without this line, .catch gets not called }) .catch(done);});it('values returned inside .then()/.catch() callbacks ' + 'are provided to the next callback', (done) => { Promise.resolve(1) .then(value => value + 1) .then(value => expect(value).toBe(2)); Promise.reject(1) .catch(value => value + 1) .then(value => expect(value).toBe(2)); setTimeout(() => { done(); }, 1);});
Хорошо, но что такое Обещание. Решение ()
и Обещание.reject ()
? Давай выясним!
it('you can use Promise.resolve() to wrap values or promises' , (done) => { function iMayReturnAPromise() { return Math.random() >= 0.5 ? Promise.resolve() : 5; }
Promise.resolve(iMayReturnAPromise()).then(done);});
it('you can use Promise.resolve() to execute something just after' , (done) => { let arr = []; Promise.resolve().then(() => arr.push(2)); arr.push(1);
setTimeout(() => { expect(arr).toEqual([1, 2]); done(); }, 1);});
/** @seehttps://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules **/it('Promise.resolve() is normally executed before setTimeout(.., 0)' , (done) => { let arr = []; setTimeout(() => arr.push('timeOut'), 0); Promise.resolve().then(() => { arr.push('resolve'); });
setTimeout(() => { expect(arr).toEqual(['resolve', 'timeOut']); done(); }, 1);});
it('you can create rejected promises', (done) => { Promise.reject('reason').catch(done);});
it('pay attention to "Uncaught (in promise) ..."', () => { Promise.reject('The error'); // Outputs in the console Uncaught (in promise) The error});
Цепи обещаний против создания новых
Хотя Новое обещание (...)
Это способ создать обещание, вы должны избегать его использования. Большую часть времени, функции/библиотеки возвращают обещание, поэтому вы должны цеплять обещания и не создавать новые:
it("Don't use new Promise(...), prefer chaining", (done) => { const url = 'https://jsonplaceholder.typicode.com/posts/1'; function badlyDesignedCustomFetch() { return new Promise((resolve, reject) => { fetch(url).then((response) => { if (response.ok) { resolve(response); } else { reject('Fetch failed'); } }); }); } function wellDesignedCustomFetch() { return fetch(url).then((response) =>; { if (!response.ok) { return Promise.reject('Fetch failed'); } return (response); }); } Promise.all([ badlyDesignedCustomFetch(), wellDesignedCustomFetch() ]).then(done);});
Но когда вы должны использовать Новое обещание (...)
? Когда вы хотите перейти от интерфейса обратного вызова к другому. См. ниже:
function imgOnLoad(img) { return new Promise((resolve, reject) => { img.onload = resolve; img.onerror = reject; });}
Параллельное исполнение
Обещание цепи приятно, но как насчет выполнения асинхронных операций параллельно? Ниже вы должны знать:
// Parallel execution of promises
it('you can use Promise.all([...]) to execute promises in parallel' , (done) => { const url = 'https://jsonplaceholder.typicode.com/posts'; const p1 = fetch(`${url}/1`); const p2 = fetch(`${url}/2`); Promise.all([p1, p2]) .then(([res1, res2]) => { return Promise.all([res1.json(), res2.json()]) }) .then(([post1, post2]) => { expect(post1.id).toBe(1); expect(post2.id).toBe(2); }) .then(done);});it('Promise.all([...]) will fail if any of the promises fails' , (done) => { const p1 = Promise.resolve(1); const p2 = Promise.reject('Error'); Promise.all([p1, p2]) .then(() => { fail('I will not be executed') }) .catch(done);});it("if you don't want Promise.all() to fail, wrap the promises " + "in a promise that will not fail", (done) => { function iMayFail(val) { return Math.random() >= 0.5 ? Promise.resolve(val) : Promise.reject(val); } function promiseOr(p, value) { return p.then(res => res, () => value); } const p1 = iMayFail(10); const p2 = iMayFail(9); Promise.all([promiseOr(p1, null), promiseOr(p2, null)]) .then(([val1, val2]) => { expect(val1 === 10 || val1 === null).toBe(true); expect(val2 === 9 || val2 === null).toBe(true); }) .catch(() => { fail('I will not be executed') }) .then(done);});it('Promise.race([...]) will resolve as soon as ' + 'one of the promises resolves o rejects', (done) => { const timeout = new Promise((resolve, reject) => setTimeout(reject, 100)); const data = fetch('https://jsonplaceholder.typicode.com/posts/1'); Promise.race([data, timeout]) .then(() => console.log('Fetch OK')) .catch(() => console.log('Fetch timeout')) .then(done);});
Синтаксис
Синтаксис обещания – это немного сложный, по сравнению с типичным синтаксисом синхронного кода. Это правда, что, цепи обещания, код сохраняет хорошую читаемость, но это может быть лучше. Новый a async синтаксис делает использование обещаний так же просто, как писать синхронный код.
// New await/async syntax
it('you can use the new await/async syntax', async () => { function timeout(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } const start = Date.now(); const delay = 200; await timeout(delay + 2); // Just some ms tolerance expect(Date.now() - start).toBeGreaterThanOrEqual(delay);});it('an async function returns a promise', (done) => { async function iAmAsync() { return 1; } iAmAsync() .then((val) => expect(val).toBe(1)) .then(done);});it('await just awaits a promise resolution', async (done) => { await Promise.resolve(); done();});it('await will throw an error if the promise fail', async(done) => { try { await Promise.reject(); fail('I will not be executed'); } catch (err) { done(); }});
Синхронные функции
На последнем рассмотрении: при разработке функции вы должны решить, является ли она синхронно или нет. Не возвращайте обещание только потому, что «вы никогда не знаете». Используйте «нормальные» синхронные функции, когда это возможно.
Все тесты – это здесь , на jsfiddle.
Это все! Надеюсь, вам понравилась эта статья.