иллюстрировать
В этой статье предполагается, что у вас есть определенные базовые знания о Promise, и она не включает объяснение API, но она будет полезна для вашего более глубокого понимания Promise.
написать впереди
После нескольких дней собеседования в компании многие люди застряли с написанным от руки обещанием (люди, которые застряли на этом вопросе, не бейте меня... я не скажу вам, в какой компании я работаю), в На самом деле, это Основная цель вопроса - изучить понимание промиса. Кстати, это изучить логику js. Написание этого бонусного пункта. Самое главное выразить свое понимание промиса.
Но реальность такова, что есть довольно много людей, которые напрямую проваливают экзамен, превращая бонусный предмет в минусовой. Писать или нет — это вопрос отношения, и достаточно написать несколько пунктов, чтобы интервьюер посмотрел на вас высоко. Далее я разберу несколько моментов, которые интервьюер надеется увидеть, и разберу Promise с точки зрения вопросов для интервью, надеюсь, это будет вам полезно.
Если вы не хотите читать длинную историю, вы можете проверить краткое содержание напрямую:Суммировать
Свойства обещания
Ошибка перехвата обещания эквивалентна попытке перехвата
- Пожалуйста, напишите вывод следующего кода
var p1 = new Promise(function(resolve, reject) {
throw Error('sync error')
})
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
2. Пожалуйста, напишите вывод следующего кода
var p1 = new Promise(function(resolve, reject) {
setTimeout(() => {
throw Error('async error')
})
})
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
- Пожалуйста, напишите вывод следующего кода
var p1 = new Promise(function(resolve, reject) {
resolve()
})
.then(res => {
throw Error('sync error')
})
Три ошибки подряд, знаете правильный ответ😏?
Правильный ответ:
- Ошибка обнаружена, и, наконец, вывод console.log
- Ошибка не может быть поймана, и консоль сообщает об ошибке
- Обещание не имеет улова, ошибка перехватывается и выдается снова, а консоль сообщает об ошибке
Основным тестом здесь является захват ошибок Promise.На самом деле, если вы думаете о захвате ошибок, который можно использовать в js, это может быть только попытка перехвата, а попытка перехвата может захватывать только синхронные ошибки и будет перехватывать, когда есть нет прослушивателя входящих ошибок. к выданной ошибке.
Таким образом, в рукописных обещаниях вы должны как минимум написать try catch для переноса обратных вызовов.
function Promise(fn) {
...
doResolve(fn, this)
}
function doResolve(fn, self) {
try {
fn(function(value) {
...
},
function(reason) {
...
})
} catch(err) {
reject(self, err)
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
try {
...
onFulfilled(value)
} catch(err) {
reject(err)
}
};
function reject(self, newValue) {
...
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
}
Обещания имеют изменения состояния
Перепишите вопрос интервью выше:
- Пожалуйста, напишите вывод следующего кода
var p1 = new Promise(function(resolve, reject) {
resolve(1)
throw Error('sync error')
})
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
- Пожалуйста, напишите вывод следующего кода
var p1 = new Promise(function(resolve, reject) {
reject(2)
resolve(1)
})
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
- Пожалуйста, напишите вывод следующего кода
var p1 = new Promise(function(resolve, reject) {
resolve(1)
})
.then(res => {
throw Error('sync error')
console.log(res)
})
.catch(err => {
console.log(err)
})
Правильный ответ:
- выход 1
- выход 2
- ошибка вывода console.log
Обещание — это контейнер с состоянием. Когда состояние заморожено, последующее разрешение или отклонение не будет инициировано. Проще говоря, одно и то же обещание может активировать только один прослушиватель состояния (onFulfilled или onRejected). Итак, в рукописном Promise должен быть тег состояния:
function Promise(fn) {
...
this._state = 0 // 状态标记
doResolve(fn, this)
}
function doResolve(fn, self) {
var done = false // 保证只执行一个监听
try {
fn(function(value) {
if (done) return
done = true
resolve(self, value)
},
function(reason) {
if (done) return;
done = true
reject(self, value)
})
} catch(err) {
if (done) return
done = true
reject(self, err)
}
}
function resolve(self, newValue) {
try {
self._state = 1;
...
}
catch(err) {
reject(self, err)
}
}
function reject(self, newValue) {
self._state = 2;
...
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
}
Обратные вызовы в методах Promise асинхронны
- Пожалуйста, напишите вывод следующего кода
var p1 = new Promise(function(resolve, reject) {
resolve()
setTimeout(() => {
console.log(1)
})
console.log(2)
})
.then(res => {
console.log(3)
})
console.log(4)
Правильный ответ:
Вывод последовательно:
2
4
3
1
или
2
4
1
3
Прежде всего, обратные вызовы в then, catch и, наконец, в promise выполняются асинхронно, поэтому предыдущий вывод2 4
Синхронный код не может быть и речи.
Тогда почему оба ответа считаются правильными, на самом деле это из-за горшка полифилла. Правильный вывод Promise должен быть 2 4 3 1, потому что Promise.then выполняется микрозадачами, а микрозадачи выполняются перед макрозадачами (setTimeout — это макрозадачи).
Однако в полифилле среда браузера не может активно регистрировать микрозадачу, поэтому fn in then также вызывается с помощью setTimeout. очередь, то в promise-polyfill Вывод 2 4 1 3 в окружении тоже считается правильным.
Затем в написанном от руки промисе обратные вызовы разрешения и отклонения должны быть установлены как асинхронные:
function handle(self, deferred) {
...
setTimeout(function() {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
(self._state === 1 ? resolve : reject)(deferred.promise, self._value);
return;
}
var ret;
try {
ret = cb(self._value);
} catch (e) {
reject(deferred.promise, e);
return;
}
resolve(deferred.promise, ret);
}, 0)
...
}
Обещания хранят возвращаемое значение
- Пожалуйста, напишите вывод следующего кода
var p1 = new Promise(function(resolve, reject) {
reject(1)
})
.catch(err => {
console.log(err)
return 2
})
setTimeout(() => {
p1
.then(res => console.log(res))
}, 1000)
Правильный ответ:
вывод 1 первый
вывод 2 через 1 секунду
Обещание сохранит последнее значение, если в следующий раз, когда метод обещания будет использоваться, он вернет обещание значения напрямую.
Итак, написанное от руки обещание, вы должны удерживать возвращаемое значение:
function Promise(fn) {
...
this._state = 0 // 状态标记
this._value = undefined; // 存储返回值
doResolve(fn, this)
}
function resolve(self, newValue) {
try {
...
if (newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(bind(then, newValue), self);
return;
}
self._state = 1;
self._value = newValue;
...
}
catch(err) {
reject(self, err)
}
}
function reject(self, newValue) {
self._state = 2;
self._value = newValue;
...
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
}
Метод Promise каждый раз возвращает новое обещание.
- Пожалуйста, напишите вывод следующего кода
var p1 = new Promise(function(resolve, reject) {
reject(1)
})
.then(
res => {
console.log(res)
return 2
},
err => {
console.log(err)
return 3
}
)
.catch(err => {
console.log(err)
return 4
})
.finally(res => {
console.log(res)
return 5
})
.then(
res => console.log(res),
err => console.log(err)
)
Правильный ответ:
Вывод последовательно:
1
undefined
3
Причина, по которой промисы могут быть объединены в цепочку, заключается в том, что каждый из их методов возвращает новое обещание, даже если это метод finally. получить параметры, потому что независимо от того, отклонит ли обещание или выполнит его, оно будет вызвано.
Итак, вам нужно вернуть новое обещание в методе обещания:
function bind(fn, thisArg) {
return function() {
fn.apply(thisArg, arguments);
};
}
function resolve(self, newValue) {
...
try {
if (newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(bind(then, newValue), self);
return;
}
self._state = 1;
...
} catch (e) {
reject(self, e);
}
}
Суммировать
Всего пять обещаний выражено выше знания:
- Ошибка перехвата обещания эквивалентна попытке перехвата
- Обещания имеют изменения состояния
- Обратные вызовы в методах Promise асинхронны
- Метод Promise каждый раз возвращает новое обещание.
- Обещания хранят возвращаемое значение
Падежи в тексте взяты изpromise-polyfill, С Meiyu впереди автор не будет показывать свои кирпичи, а также напомнить всем интервьюерам смотреть на исходники отличных работ, почему бы не посмотреть на те менее формальные сторонние реализации.
Ведь цель компании не в том, чтобы создавать повторяющиеся колеса.Если вы сможете четко и ясно изложить некоторые из вышеперечисленных знаний, то можно считать, что вы уже являетесь разработчиком, умеющим правильно и гибко использовать Promise.Примите цели компании по подбору персонала как например, и я полагаю, что большинству компаний это также необходимо).
наконец:
2019 уже почти наступил, желаю всем удачи! 🎉🎉🎉
-- The End