Все знакомы с рукописными обещаниями. По сути, будь то крупная фабрика или маленькая фабрика, рукописные обещания стали обязательным элементом знаний для интервью. Я слышал, ты еще многого не знаешь? Так что давай, разблокируй Promise с нуля!
Чтобы просмотреть полный код, нажмите:GitHub.com/Ци Руохан/AR…
Общие вопросы об обещании интервью
Прежде всего, мы возьмем в качестве отправной точки стандартные вопросы интервью на Promise и посмотрим, что интервьюеры любят тестировать:
- Какую проблему решает Promise?
- Каковы отраслевые реализации Promises?
- Каковы наиболее часто используемые API для промисов?
- Можете ли вы написать обещание, соответствующее спецификации Promise/A+?
- Каков процесс выполнения промисов в цикле событий?
- Каковы подводные камни промисов и как их можно решить?
Давайте рассмотрим эти вопросы один за другим:
Причины обещаний и реализации в отрасли
До промисов код выглядел так, когда мы имели дело с вложением нескольких асинхронных запросов. . .
let fs = require('fs')
fs.readFile('./name.txt','utf8',function(err,data){
fs.readFile(data, 'utf8',function(err,data){
fs.readFile(data,'utf8',function(err,data){
console.log(data);
})
})
})
Чтобы получить результат обратного вызова, нам приходится вкладывать слой за слоем, что, можно сказать, довольно отвратительно. И в основном мы должны выполнять серию обработок по результату каждого запроса, что делает код более трудным для чтения и трудным для обслуживания, что печально известно в легенде.ад обратного звонка~ производитьад обратного звонкаПричина сводится к двум моментам:
- вложенные вызовы, выход первой функции часто является входом второй функции;
- Одновременная обработка нескольких асинхронных запросов, часто бывает необходимо синхронно запрашивать конечный результат во время разработки.
После анализа причины решение проблемы предельно ясно:
- Избавьтесь от вложенных вызовов: Это можно решить с помощью цепного вызова Promise;
- Объединение результатов запроса из нескольких задач: Используйте Promise.all для обработки ошибок при объединении нескольких задач.
Promise решает проблему асинхронного вложения за счет более удобной организации кода.
Давайте посмотрим, как выглядит приведенный выше пример с промисами:
let fs = require('fs')
function read(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, 'utf8', (err, data) => {
if (err) reject(err);
resolve(data);
})
})
}
read('./name.txt').then((data)=>{
return read(data)
}).then((data)=>{
return read(data)
}).then((data)=>{
console.log(data);
},err=>{
console.log(err);
})
Раздутая вложенность стала более линейной.Есть что-нибудь? Да он наш асинхронный артефакт Promise!
Снова вернемся к предыдущему вопросу,Какую проблему решает для нас Promise?В традиционном асинхронном программировании, если существует зависимость между асинхронностью, эта зависимость должна быть удовлетворена путем вложения обратных вызовов слой за слоем. Если вложенных слоев слишком много, удобочитаемость и ремонтопригодность ухудшатся. , что приведет к так называемому «обратному вызову». hell", а Promise изменяет вложенные вызовы на связанные вызовы, повышая читабельность и удобство сопровождения. Другими словами, промисы решают проблему асинхронного стиля кодирования.Каковы отраслевые реализации Promises?Известные в отрасли библиотеки классов, реализующие Promise, включают bluebird, Q и ES6-Promise.
С нуля, рукописные обещания
Promise/A+
Если мы хотим написать обещание вручную, мы должны следоватьPromise/A+Спецификация, все библиотеки классов Promise в отрасли следуют этой спецификации.
На самом деле, спецификация Promise/A+ очень подробно объясняет, как реализовать стандартную библиотеку классов Promise. Каждую строку кода можно проследить в спецификации Promise/A+, поэтому в следующем процессе реализации я постараюсь максимально сопоставить код со спецификацией Promise/A+.
А теперь к делу~
Основные обещания
Давайте сначала рассмотрим самый простой способ использования промисов:
const p1 = new Promise((resolve, reject) => {
console.log('create a promise');
resolve('成功了');
})
console.log("after new promise");
const p2 = p1.then(data => {
console.log(data)
throw new Error('失败了')
})
const p3 = p2.then(data => {
console.log('success', data)
}, err => {
console.log('faild', err)
})
Вывод консоли:
"create a promise"
"after new promise"
"成功了"
"faild Error: 失败了"
- Во-первых, когда мы вызываем Promise, мы возвращаем объект Promise.
- При создании объекта Promise вам необходимо передатьисполнительная функция, основные бизнес-процессы Promise выполняются в функции-исполнителе.
- Если выполнение дела, запущенного в функции-исполнителе, будет успешным, будет вызвана функция разрешения; если выполнение завершится ошибкой, будет вызвана функция отклонения.
- Состояние Promise необратимо, и функция разрешения и функция отклонения вызываются одновременно, а результат первого вызова будет принят по умолчанию.
Вышеприведенное кратко представляет некоторые из основных методов использования промисов в сочетании сPromise/A+Спецификация, мы можем проанализировать основные характеристики Promise:
- Промис имеет три состояния:
pending
,fulfilled
, илиrejected
;"Обещание спецификации/A+ 2.1"new promise
, вам нужно пройтиexecutor()
executor, исполнитель выполняется немедленно;executor
Принимает два параметра, которыеresolve
иreject
;- Состояние обещания по умолчанию:
pending
;- обещание имеет
value
Значение для сохранения статуса успеха может бытьundefined/thenable/promise
;"Обещание спецификации/A+ 1.3"- обещание имеет
reason
Сохраните значение неудачного состояния: «Specification Promise/A+ 1.5».- обещания могут быть только
pending
прибытьrejected
, или изpending
прибытьfulfilled
, как только состояние будет подтверждено, оно не изменится;- обещание должно иметь
then
метод, затем получает два параметра, а именно обратный вызов onFulfilled, когда обещание выполняется успешно, и обратный вызов onRejected, когда обещание не выполняется; «Promise/A+ 2.2»- Если обещание успешно выполнено при вызове, выполните
onFulfilled
, параметрpromise
изvalue
;- Если обещание не выполнено к тому времени, когда вызывается then, тогда выполните
onRejected
, параметрpromise
изreason
;- Если затем возникнет исключение, оно будет передано в качестве параметра неудачному обратному вызову следующего затем.
onRejected
;
В соответствии с вышеперечисленными признаками мы пытаемся наметить форму Обещания:
// 三个状态:PENDING、FULFILLED、REJECTED
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 默认状态为 PENDING
this.status = PENDING;
// 存放成功状态的值,默认为 undefined
this.value = undefined;
// 存放失败状态的值,默认为 undefined
this.reason = undefined;
// 调用此方法就是成功
let resolve = (value) => {
// 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
// 调用此方法就是失败
let reject = (reason) => {
// 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
try {
// 立即执行,将 resolve 和 reject 函数传给使用者
executor(resolve,reject)
} catch (error) {
// 发生异常时执行失败逻辑
reject(error)
}
}
// 包含一个 then 方法,并接收两个参数 onFulfilled、onRejected
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
}
}
После написания кода мы можем протестировать его:
const promise = new Promise((resolve, reject) => {
resolve('成功');
}).then(
(data) => {
console.log('success', data)
},
(err) => {
console.log('faild', err)
}
)
Вывод консоли:
"success 成功"
Сейчас мы реализовали базовую версию Promise, но не радуйтесь, здесь мы имеем дело только с промисами для синхронных операций. если вexecutor()
Если передается асинхронная операция, попробуем:
const promise = new Promise((resolve, reject) => {
// 传入一个异步操作
setTimeout(() => {
resolve('成功');
},1000);
}).then(
(data) => {
console.log('success', data)
},
(err) => {
console.log('faild', err)
}
)
После выполнения тестового скрипта было обнаружено, что промис ничего не возвращает.
Потому что, когда обещание вызывает метод then, текущее обещание не было успешным и находилось в состоянии ожидания. Поэтому, если при вызове метода then текущее состояние находится в ожидании, нам нужно сначала сохранить успешные и неудачные обратные вызовы отдельно, вexecutor()
Когда выполняется асинхронная задача, запускается разрешение или отклонение, и, в свою очередь, вызывается обратный вызов для успеха или неудачи.
Объединив эту идею, давайте оптимизируем код:
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
// 存放成功的回调
this.onResolvedCallbacks = [];
// 存放失败的回调
this.onRejectedCallbacks= [];
let resolve = (value) => {
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 依次将对应的函数执行
this.onResolvedCallbacks.forEach(fn=>fn());
}
}
let reject = (reason) => {
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 依次将对应的函数执行
this.onRejectedCallbacks.forEach(fn=>fn());
}
}
try {
executor(resolve,reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
// 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value)
});
// 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
this.onRejectedCallbacks.push(()=> {
onRejected(this.reason);
})
}
}
}
есть тест:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
},1000);
}).then(
(data) => {
console.log('success', data)
},
(err) => {
console.log('faild', err)
}
)
консоль ждет1s
Почтовый вывод:
"success 成功"
в порядке! Готово, проблема с асинхронностью решена!
Учащиеся, знакомые с шаблонами проектирования, должны понимать, что на самом деле этомодель публикации-подписки, это收集依赖 -> 触发通知 -> 取出依赖执行
Он широко используется при реализации режима публикации-подписки.
Функция проникновения связанного вызова и стоимости тогда
Все мы знаем, что преимущество промисов в том, что их можно связать. Когда мы используем Promise, когда функция then возвращает значение, независимо от того, какое это значение, мы можем получить его в следующем then, который называетсяЦепочка вызовов then. Кроме того, когда мы не вводим параметры then , например:promise.then().then()
, то следующее then все еще может получить значение, возвращенное предыдущим then, которое называетсяПроникновение ценностей. Как этого добиться? Просто подумайте об этом, если мы воссоздаем объект промиса каждый раз, когда вызываем then, и передаем результат возврата предыдущего then методу then этого нового промиса, сможем ли мы продолжить? Итак, давайте попробуем воплотить это в жизнь. Это также высший приоритет написания исходного кода Promise от руки, так что взбодритесь и приходите на главное событие!
С вышеуказанными идеями мы объединяемPromise/A+Спецификация, чтобы разобраться в идее:
- параметры тогда
onFulfilled
иonRejected
может быть по умолчанию, еслиonFulfilled
илиonRejected
Это не функция, игнорируйте ее, и вы все равно можете получить ранее возвращенное значение в следующем: "Specification Promise/A+ 2.2.1, 2.2.1.1, 2.2.1.2"- Промис может быть then несколько раз, и каждый раз, когда выполняется метод promise.then, возвращается «новое промис»; «Specification Promise/A+ 2.2.7»
- Если возвращаемое значение x из then является обычным значением, то результат будет передан в качестве параметра следующему успешному обратному вызову then;
- Если возникает исключение, то оно передается как параметр неудачному обратному вызову следующего then; «Specification Promise/A+ 2.2.7.2»
- Если возвращаемое значение x of then является промисом, он будет ждать завершения выполнения промиса. затем: "Spec Promise/A+ 2.2.7.3, 2.2.7.4"
- Если возвращаемое значение x of then и promise являются одним и тем же ссылочным объектом, что приводит к циклической ссылке, будет выдано исключение, и это исключение будет передано неудачному обратному вызову следующего then; «Promise/A+ 2.3.1»
- Если возвращаемое значение x из then является обещанием, а x вызывает и разрешение, и отклонение, первый вызов имеет приоритет, а все остальные вызовы игнорируются; «Promise/A+ 2.3.3.3.3»
Завершим код:
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
const resolvePromise = (promise2, x, resolve, reject) => {
// 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise Promise/A+ 2.3.1
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// Promise/A+ 2.3.3.3.3 只能调用一次
let called;
// 后续的条件要严格判断 保证代码能和别的库一起使用
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
// 为了判断 resolve 过的就不用再 reject 了(比如 reject 和 resolve 同时调用的时候) Promise/A+ 2.3.3.1
let then = x.then;
if (typeof then === 'function') {
// 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty Promise/A+ 2.3.3.3
then.call(x, y => { // 根据 promise 的状态决定是成功还是失败
if (called) return;
called = true;
// 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
resolvePromise(promise2, y, resolve, reject);
}, r => {
// 只要失败就失败 Promise/A+ 2.3.3.3.2
if (called) return;
called = true;
reject(r);
});
} else {
// 如果 x.then 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.3.4
resolve(x);
}
} catch (e) {
// Promise/A+ 2.3.3.2
if (called) return;
called = true;
reject(e)
}
} else {
// 如果 x 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.4
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks= [];
let resolve = (value) => {
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
}
let reject = (reason) => {
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
}
try {
executor(resolve,reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
//解决 onFufilled,onRejected 没有传值的问题
//Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
//因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后 then 的 resolve 中捕获
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
// 每次调用 then 都返回一个新的 promise Promise/A+ 2.2.7
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
//Promise/A+ 2.2.2
//Promise/A+ 2.2.4 --- setTimeout
setTimeout(() => {
try {
//Promise/A+ 2.2.7.1
let x = onFulfilled(this.value);
// x可能是一个proimise
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
//Promise/A+ 2.2.7.2
reject(e)
}
}, 0);
}
if (this.status === REJECTED) {
//Promise/A+ 2.2.3
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
}, 0);
}
if (this.status === 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;
}
}
есть тест:
const promise = new Promise((resolve, reject) => {
reject('失败');
}).then().then().then(data=>{
console.log(data);
},err=>{
console.log('err',err);
})
Вывод консоли:
"失败 err"
На данный момент мы выполнили самую критическую часть обещания: цепочку вызовов then и проникновение значений. Как только вы разберетесь с цепочкой вызовов then и проникновением значений, вы также разберетесь с промисами.
Проверка того, что обещание соответствует спецификации
Спецификация Promise/A+ предоставляет специальный тестовый сценарий, который проверяет, соответствует ли написанный код спецификации Promise/A+.
Во-первых, в коде реализации промиса добавьте следующий код:
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
Установите тестовый скрипт:
npm install -g promises-aplus-tests
Если текущий исходный файл обещания называется promise.js
Затем выполните следующую команду в соответствующем каталоге:
promises-aplus-tests promise.js
Всего в promises-aplus-tests содержится 872 тестовых примера. Вышеприведенный код может идеально пройти все варианты использования.
Спасибо за напоминание, потому что использование setTimeout в статье для реализации асинхронности промисов вызовет у всех непонимание. Так что добавьте некоторые аннотации здесь!
Поскольку нативный Promise — это микрозадача, предоставляемая движком V8, мы не можем восстановить реализацию движка V8, поэтому здесь мы используем setTimeout для имитации асинхронности, поэтому нативная — это микрозадача, а здесь — макрозадача.
Также упоминается в спецификации Promise A+ 3.1:
Этого можно достичь с помощью механизма «макрозадачи» (например, setTimeout или setImmediate) или механизма «микрозадачи» (например, MutatonObserver или process.nextTick ).
Если вы хотите реализовать обещанные микрозадачи, вы можете использовать MutationObserver вместо seiTimeout для реализации микрозадач.
Небольшой партнер сказал, что можно использовать queueMicrotask для реализации микрозадач, я тоже проверил некоторую информацию, и это возможно. Однако совместимость queueMicrotask не очень хорошая, и он вообще не поддерживается в IE. Насколько я знаю, полифилл очереди Микротаск основан на промисе, если промис не поддерживается, он будет конвертирован в setTimeout.
В целом и queueMicrotask, иmutationObserver могут реализовывать механизм микрозадач, но навязчивым партнерам рекомендуется использовать для его реализации мутациюObserver, а тем, кто не навязчив, следует понимать микрозадачный механизм обещания~
API промисов
Хотя приведенный выше исходный код промиса уже соответствует спецификации Promise/A+, родной промис также предоставляет некоторые другие методы, такие как:
- Promise.resolve()
- Promise.reject()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.all()
- Обещание.гонка()
Реализация каждого метода подробно описана ниже:
Promise.resolve
По умолчанию создает успешное обещание.
static resolve(data){
return new Promise((resolve,reject)=>{
resolve(data);
})
}
Здесь следует отметить, что,promise.resolve имеет функцию ожидания. Если параметр является обещанием, он будет ждать, пока обещание будет разрешено, и будет выполнено вниз, поэтому здесь вам нужно выполнить небольшую обработку в методе разрешения:
let resolve = (value) => {
// ======新增逻辑======
// 如果 value 是一个promise,那我们的库中应该也要实现一个递归解析
if(value instanceof Promise){
// 递归解析
return value.then(resolve,reject)
}
// ===================
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
}
есть тест:
Promise.resolve(new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok');
}, 3000);
})).then(data=>{
console.log(data,'success')
}).catch(err=>{
console.log(err,'error')
})
консоль ждет3s
Почтовый вывод:
"ok success"
Promise.reject
По умолчанию генерируется невыполненное обещание, и Promise.reject напрямую превращает значение в результат ошибки.
static reject(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
Promise.prototype.catch
Promise.prototype.catch используется для перехвата исключений промисов,эквивалентно неудачному тогда.
Promise.prototype.catch = function(errCallback){
return this.then(null,errCallback)
}
Promise.prototype.finally
final означает не окончательный смысл, а смысл, который все равно будет исполнен. Возврат обещания также будет ждать выполнения обещания. Если возвращено успешное обещание, будет использоваться предыдущий результат; если возвращено неудачное обещание, неудачный результат будет использован и передан в catch.
Promise.prototype.finally = function(callback) {
return this.then((value)=>{
return Promise.resolve(callback()).then(()=>value)
},(reason)=>{
return Promise.resolve(callback()).then(()=>{throw reason})
})
}
есть тест:
Promise.resolve(456).finally(()=>{
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(123)
}, 3000);
})
}).then(data=>{
console.log(data,'success')
}).catch(err=>{
console.log(err,'error')
})
консоль ждет3s
Почтовый вывод:
"456 success"
Promise.all
promise.all должен решить проблему параллелизма, множественный асинхронный одновременный доступ к конечному результату (сбой, если один сбой).
Promise.all = function(values) {
if (!Array.isArray(values)) {
const type = typeof values;
return new TypeError(`TypeError: ${type} ${values} is not iterable`)
}
return new Promise((resolve, reject) => {
let resultArr = [];
let orderIndex = 0;
const processResultByKey = (value, index) => {
resultArr[index] = value;
if (++orderIndex === values.length) {
resolve(resultArr)
}
}
for (let i = 0; i < values.length; i++) {
let value = values[i];
if (value && typeof value.then === 'function') {
value.then((value) => {
processResultByKey(value, i);
}, reject);
} else {
processResultByKey(value, i);
}
}
});
}
есть тест:
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok1');
}, 1000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('ok2');
}, 1000);
})
Promise.all([1,2,3,p1,p2]).then(data => {
console.log('resolve', data);
}, err => {
console.log('reject', err);
})
консоль ждет1s
Почтовый вывод:
"resolve [ 1, 2, 3, 'ok1', 'ok2' ]"
Promise.race
Promise.race используется для обработки нескольких запросов, и используется самый быстрый (тот, кто финиширует первым).
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
// 一起执行就是for循环
for (let i = 0; i < promises.length; i++) {
let val = promises[i];
if (val && typeof val.then === 'function') {
val.then(resolve, reject);
} else { // 普通值
resolve(val)
}
}
});
}
Особое примечание: потому чтоПромисы не имеют метода прерывания, xhr.abort(), ajax имеют собственные методы прерывания, axios реализован на основе ajax, fetch основан на обещании, поэтому его запрос нельзя прервать.
Это также недостаток обещания, мы можем использовать гонку, чтобы самостоятельно инкапсулировать метод прерывания:
function wrap(promise) {
// 在这里包装一个 promise,可以控制原来的promise是成功还是失败
let abort;
let newPromise = new Promise((resolve, reject) => { // defer 方法
abort = reject;
});
let p = Promise.race([promise, newPromise]); // 任何一个先成功或者失败 就可以获取到结果
p.abort = abort;
return p;
}
const promise = new Promise((resolve, reject) => {
setTimeout(() => { // 模拟的接口调用 ajax 肯定有超时设置
resolve('成功');
}, 1000);
});
let newPromise = wrap(promise);
setTimeout(() => {
// 超过3秒 就算超时 应该让 proimise 走到失败态
newPromise.abort('超时了');
}, 3000);
newPromise.then((data => {
console.log('成功的结果' + data)
})).catch(e => {
console.log('失败的结果' + e)
})
консоль ждет1s
Почтовый вывод:
"成功的结果成功"
promisify
Promisify — это способ преобразовать API в узле в обещание.
В Node версии 12.18 и выше уже поддерживается нативный метод промисификации:const fs = require('fs').promises
.
const promisify = (fn) => { // 典型的高阶函数 参数是函数 返回值是函数
return (...args)=>{
return new Promise((resolve,reject)=>{
fn(...args,function (err,data) { // node中的回调函数的参数 第一个永远是error
if(err) return reject(err);
resolve(data);
})
});
}
}
Что делать, если вы хотите преобразовать все API в узле в промисы:
const promisifyAll = (target) =>{
Reflect.ownKeys(target).forEach(key=>{
if(typeof target[key] === 'function'){
// 默认会将原有的方法 全部增加一个 Async 后缀 变成 promise 写法
target[key+'Async'] = promisify(target[key]);
}
});
return target;
}
резюме
Написав почти 20 000 слов, мы наконец-то можем положить конец написанному от руки обещанию. Мы начнем с использования обещания, создадим общую структуру обещания, а затем наполним код в соответствии со спецификацией обещания/A+, сосредоточив внимание на реализацииЦепочка вызовов then и проникновение значений; Затем используйте тестовый скрипт, чтобы проверить, соответствует ли написанный код спецификации; наконец, завершите реализацию Promise API. Разбираться в обещаниях на самом деле не так уж и сложно, в конечном счете лучше сделать идеально, и для этого нужно больше практики.
Поскольку места слишком много, в этой статье в основном говорится о 1, 2, 3, 4 и 6 вопросах интервью.Что касается пятого пункта, я систематически разберу его в статье о EventLoop.Я думаю, что после того, как вы видел Promise После исходного кода будет проще разобраться в EventLoop.
Статьи с похожим содержанием для запланированного результата:
- Механизм цикла JS EventLoop (основной поток, микрозадача, рендеринг, макрозадача).
- Реализация генератора/асинхронного+ожидания.
Если вам интересны мои статьи, подписывайтесь на меня! Если у вас есть какие-либо вопросы по статье, пожалуйста, оставьте мне сообщение~
Ссылаться на
9k слов | Анализ принципа реализации Promise/async/Generator
Обещание: обещание, которое вы можете понять
Реализация исходного кода Promise (полностью соответствует спецификации Promise/A+)
о
Автор Ци Сяошэнь, фронтенд программы Юань.
Немного литературно, как фотография. Хотя сейчас она работает с 9 до 5 и усердно учится, ее мечта — стать героиней, помогать бедным и помогать бедным и путешествовать по миру с мечами. Я надеюсь, что однажды я смогу исправить ошибку и реализовать свою мечту.
публика:Большое переднее пространство, обновляйте время от времени, добро пожаловать в игру~