передняя часть Seewo ENOW
Официальный сайт компании:CVTE (Гуанчжоу CVTE)
Команда: enow team центра программных платформ Seewo для будущего образования в рамках CVTE
Автор этой статьи:
предисловие
Promise — это решение для асинхронного программирования, которое можно рассматривать как контейнер, в котором хранятся результаты будущих событий.
Он имеет три состояния:pending
(в ходе выполнения),fulfilled
(успешно) иrejected
(Не удалось), после того как состояние изменилось, его нельзя изменить снова.
Что такое ад обратного вызова?
При работе с асинхронными запросами мы обычно используем для этого callback-функции, так что в этом нет ничего плохого, и логика ясна.
http.post(data,function(res) {
// do something here
})
Но если нам нужно будет инициировать следующий запрос на основе результата, возвращаемого предыдущим запросом, код становится таким:
http.post(data,function(res1) {
http.post(res1,function(res2) {
// do something here
})
})
По мере усложнения продукта и бизнес-логики этот код может породить:
http.post(data,function(res1){
http.post(res1,function(res2){
http.post(res2,function(res3){
http.post(res3,function(res4){
http.post(res4,function(res5){
http.post(res5,function(res6){
// do something here
})
})
})
})
})
})
это печально известноад обратного звонкаНегативные последствия очевидны:
- Код раздут и плохо читаем
- Высокая степень сцепления, плохая ремонтопригодность
- Исключения могут обрабатываться только внутри функции обратного вызова.
Используя обещания, мы можем написать это:
fetch(data).then(res1 => {
return fetch(res1);
}).then(res2 => {
return fetch(res2);
}).then(res3 => {
return fetch(res3);
}).catch(err => {
console.error('err: ', err);
})
Есть ли недостатки у обещаний?
Promise
Цепочка вызовов может сделать код более интуитивным, хотя он кажется логически более ясным, он все же существует.then
Цепочка вызовов имеет проблему избыточности кода и следующие недостатки:
- Обещание не может быть отменено, как только оно будет создано, оно будет выполнено немедленно и не может быть отменено на полпути.
- Если функция обратного вызова не установлена, ошибка, выданная внутри промиса, не будет отражена снаружи.
- Когда он находится в ожидании государства, невозможно узнать, какой этап в настоящее время находится (только начинается или собирается завершить).
Каков результат этого кода? Почему?
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
setTimeout(() => {
console.log(2);
})
reject('error');
})
promise.then(() => {
console.log(3);
}).then(() => {
console.log(5)
}).catch(e => console.log(e))
console.log(4);
Клише, то есть рассмотрение макрозадач и микрозадач. Основные моменты:
- PromiseКод в теле функции выполняется синхронно
- выполнить первымзадача макроса, а затем выполнитьмикрозадачи, выполнятьмикрозадачиПосле этого проверьте еще раз, есть ли что-то, что нужно выполнитьзадача макроса, и так далее
- PromiseПосле изменения состояния его нельзя изменить снова
Правильный порядок вывода1、4、3、5、2
Могу ли я написать промисы от руки?
Вот простая реализация, которая может удовлетворитьthen
метод прикованPromise
:
class Promise {
constructor(params) {
//初始化state为pending
this.state = 'pending';
//成功的值,返回一般都是undefined
this.value = undefined;
//失败的原因,返回一般都是undefined
this.reason = undefined;
//成功执行函数队列
this.onResolvedCallbacks = [];
//失败执行函数队列
this.onRejectedCallbacks = [];
//success
let resolve = value => {
if (this.state === 'pending') {
//state change
this.state = 'fulfilled';
//储存成功的值
this.value = value;
//一旦成功,调用函数队列
this.onResolvedCallbacks.forEach(fn => fn());
}
};
//error
let reject = reason => {
if (this.state === 'pending') {
//state change
this.state = 'rejected';
//储存成功的原因
this.reason = reason;
//一旦失败,调用函数队列
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
params(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};
let promise2 = new Promise((resolve, reject) => {
//当状态是fulfilled时执行onFulfilled函数
if (this.state === 'fulfilled') {
//异步实现
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
//当状态是rejected时执行onRejected函数
if (this.state === 'rejected') {
//异步实现
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
//当状态是pending时,往onFulfilledCacks、onRejectedCacks里加入函数
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
//异步实现
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
//异步实现
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
};
});
return promise2;
}
catch(fn) {
return this.then(null, fn);
}
}
function resolvePromise(promise2, x, resolve, reject) {
//循环引用报错
if (x === promise2) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
//防止多次调用
let called;
//判断x
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, err => {
if (called) return;
called = true;
reject(err);
})
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
//resolve方法
Promise.resolve = function (val) {
return new Promise((resolve, reject) => {
resolve(val)
});
}
//reject方法
Promise.reject = function (val) {
return new Promise((resolve, reject) => {
reject(val);
});
}
const test = new Promise((res, rej) => {
setTimeout(() => {
res('resolve after 2000ms');
}, 2000)
})
test.then(res => {
console.error('res: ', res); // res: resolve after 2000ms
})
Здесь наша функция обратного вызова используетsetTimeout
реализации, поместите их в очередь макрозадач, есть ли способ поместить их в очередь микрозадач? Как сделать? Заинтересованная детская обувь может попытаться добитьсяСпецификация Обещание/A+изPromise
.
Существуют ли какие-либо другие асинхронные решения, кроме промисов?
также можно использоватьES6серединаGeneratorиметь дело с,Generator Реализация несколько похожа на реализацию традиционных языков программирования.сопрограмма.сопрограммаЭтапы выполнения примерно такие:
- Корутина АНачать выполнение, выполнить до того места, где его нужно приостановить
- Корутина АПриостановлено, исполнение переданоКорутина Б
- Корутина БПосле выполнения вернуть право исполненияКорутина А
- Корутина Авозобновить выполнение, вернуть результат
JavascriptАсинхронная задача аналогична приведенной выше.Корутина А, Разделен на два (или больше разделов) выполнено.
Generatorа такжеPromiseАналогично, можно рассматривать как контейнер, разница в том, чтоGeneratorконтейнер используется для храненияасинхронная задачавместоусловие. Если требуется асинхронная операция, используйтеyield
Просто отдайте контроль и используйтеnext
метод может вернуть управление, возобновить выполнение иnext
Параметры метода можно использовать как и предыдущийyield
Возвращаемое значение выражения.
Все тот же пример, мы используемGeneratorЧтобы получить волну:
function* getData(data) {
const res1 = yield http.post(data);
const res2 = yield http.post(res1);
const res3 = yield http.post(res2);
const res4 = yield http.post(res3);
return http.post(res4);
}
const g = getData(123);
const res1 = g.next(); // {value: res1,done: false}
const res2 = g.next(res1.value); // {value: res2,done: false}
const res3 = g.next(res2.value); // {value: res3,done: false}
const res4 = g.next(res3.value); // {value: res4,done: false}
const res5 = g.next(res4.value); // {value: res5,done: true}
const res6 = g.next() // {value: undefined,done: true}
при звонкеgetData
, он не возвращает результат, а возвращает объект-указательg
. объект указателяg
Вверхnext
метод, вы можете сделать так, чтобы внутренний указатель указывал на следующийyield
оператор и возвращает объект, представляющий текущее состояние выполнения.value
недвижимость актуальнаyield
значение выражения,done
Свойство указывает, завершен ли текущий обход. Когда обход окончен, если вы продолжаете звонитьnext
метод, он вернетundefined
.
также,GeneratorОн также может быть прекращен досрочно, просто позвоните вreturn
метод, который возвращаетdone
собственностьtrue
, затем позвоните еще разnext
метод всегда возвращаетdone
дляtrue
.
function* getData(data) {
yield 1;
yield 2;
yield 3;
}
const g = getData();
g.next(); // {value: 1, done: false}
g.return('done'); // {value: 'done', done: true}
g.next(); // {value: undefined, done: true}
Для отлова ошибок,GeneratorОшибки могут быть перехвачены извне
function* getData(data) {
try{
const res = yield data;
} catch(error) {
console.error('inner catch error: ', error);
}
}
const g = getData(123);
g.next();
try {
g.throw('err1'); // inner catch error: throw err
g.throw('err2') // outer catch error: throw err
} catch (error) {
console.error('outer catch error: ', error);
}
Вы понимаете асинхронность/ожидание?
async
Что такое функция? Проще говоря, этоGenerator
Синтаксический сахар для функций.Generator
Использование все еще немного неясно, и оно всегда кажется немного сложным в использовании, поэтомуES7запущен вasync/await
. Грамматика такжеGenerator
похоже, просто*
заменитьasync
,yield
заменитьawait
. ноGenerator
возвращает итератор иasync/await
возвращаетPromise
объект, что означает, что вы можете использоватьthen
,catch
Ждём метод.
Generator
Выполнение функции зависит от исполнителя. а такжеasync
У функции есть свой исполнитель, которыйGenerator
функция и автоисполнитель, завернутые в функцию, поэтому не нужно использоватьnext
метод для постепенного управления выполнением функции, что согласуется с вызовом обычных функций.
async function getData(data) {
const res1 = await fetch(data);
const res2 = await fetch(res1);
const res3 = await fetch(res2);
const res4 = await fetch(res3);
const res5 = await fetch(res4);
return res5;
}
const finalRes = getData(123);
Суммировать
Окончательное решение асинхронного программирования заключается в обработке асинхронных операций, таких как синхронное программирование.