1. Предыстория и спецификация генерации обещаний
Как мы все знаем, промисы — это новая функция, представленная в ES6 для решения проблемы ада обратных вызовов. Вот простой пример: Последовательность вызова интерфейса управления:
апиА-->апиВ-->апиС. Сложный бизнес, разработчики взломают. Молодое поколение отдает здесь дань уважения старшим.
// 回调地狱
apiA({
handleSuccess(resA){
apiB({
handleSuccess(resB){
apiC({
handleSuccess(resC){
}
})
}
})
}
})
следовательноPromise/A+Спецификация появилась, и ES6 Promise был разработан в соответствии со спецификацией.
2. Синхронизируйте обещания
Прочтите спецификацию, чтобы получить следующие основные требования:
- Обещание имеет три состояния: ожидание (состояние ожидания), выполнено (состояние успеха), отклонено (состояние отказа).
- pending является начальным состоянием и может быть преобразовано в выполненное и отклоненное
- В случае успеха его нельзя изменить на другое состояние, и оно должно иметь неизменяемое значение (value)
- Когда он выходит из строя, его нельзя изменить на другое состояние, и должна быть неизменная причина (причина)
- новое обещание (исполнитель = (разрешить, отклонить) => {разрешить (значение)}), разрешить (значение) устанавливает статус выполнено
- new Promise(executor=(resolve,reject)=>{reject(reson)}), reject(reson) устанавливает состояние на отклоненное
- Если исполнитель работает ненормально, выполните reject()
- тогда можно: тогда (onFulfilled, onRejected)
- onFulfilled: статус выполнен, выполнить onFulfilled и передать значение
- onRejected: если статус отклонен, выполнить onRejected и передать причину
// 1.Promise存在三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态)
const STATUS_PENDING = 'pending'
const STATUS_FULFILLED = 'fulfilled'
const STATUS_REJECTED = 'rejected'
class myPromise {
constructor(executor) {
// pending为初始态,并可以转化为fulfilled和rejected
this.status = STATUS_PENDING
this.value = '' // 3
this.reason = '' // 4
let resolve = value => {
// 5.
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED
this.value = value
}
}
let reject = reason => {
//6.
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED
this.reason = reason
}
}
// 7.
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
// 8.
then(onFulfilled = () => {}, onRejected = () => {}) {
// 8.1
if (this.status === STATUS_FULFILLED) {
onFulfilled(this.value)
}
// 8.2
if (this.status === STATUS_REJECTED) {
onRejected(this.reason)
}
}
}
new myPromise(resolve => {
console.log('before resolve')
resolve(1)
}).then(res => {
console.log(res)
})
new myPromise((resolve, reject) => {
console.log('before reject')
reject('reject error')
}).then(res => {
console.log(res)
}, error => {
console.log(error)
})
3. Асинхронные обещания
new myPromise(resolve => {
console.log('before resolve')
setTimeout(()=>{
resolve(1)
},1000)
}).then(res => {
console.log(res)
})
Состояние обещания можно изменить только при его разрешении или отклонении.Когда синхронный код выполняется для обратного вызова then, состояние обещания все еще находится в ожидании, и детали не соответствуют нашим ожиданиям.
Если бы это были вы, как бы вы решили эту проблему? Например, вы имеете дело с кучей дел (состояние pending), и тут (тогда) начальник сказал: Заканчивай поставленную задачу за одну секунду и приходи ко мне в кабинет, если не справишься, убирайся . Если вы боитесь забыть, вы обычно используете список для записи onResolvedCallbacks = ['заканчивайте работу, идите в кабинет начальника'], onRejectedCallbacks = ['не могу закончить, уходите'], см. завершение результат через 1 секунду, а затем выберите следующий шаг в соответствии с .
Завершенный код выглядит следующим образом:
const STATUS_PENDING = 'pending'
const STATUS_FULFILLED = 'fulfilled'
const STATUS_REJECTED = 'rejected'
class myPromise {
constructor(executor) {
this.status = STATUS_PENDING
this.value = ''
this.reason = ''
// 成功存放的数组
this.onResolvedCallbacks = [];
// 失败存放法数组
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_FULFILLED
this.value = value
// pending->fulfilled 按照成功清单执行
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = reason => {
if (this.status === STATUS_PENDING) {
this.status = STATUS_REJECTED
this.reason = reason
// pending->rejected 按照异常清单执行
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled = () => {}, onRejected = () => {}) {
if (this.status === STATUS_FULFILLED) {
onFulfilled(this.value)
}
if (this.status === STATUS_REJECTED) {
onRejected(this.reason)
}
// 忙碌状态,先记录老板吩咐的内容
if (this.status === STATUS_PENDING) {
// onFulfilled传入到成功数组
this.onResolvedCallbacks.push(() => onFulfilled(this.value))
// onRejected传入到失败数组
this.onRejectedCallbacks.push(() => onRejected(this.reason))
}
}
}
// 异步
new myPromise((resolve, reject) => {
console.log('老板曰: 一秒做完手上的事来一下我办公室,做不完滚蛋')
setTimeout(() => {
if (false) { // 臣妾做不到啊
resolve('做完了手上的事,去老板办公室')
} else {
reject('做不完,滚蛋')
}
}, 1000)
}).then(res => {
console.log(`1s 后:${res}`)
}, error => {
console.log(`1s 后:${error}`)
})
// 老板曰: 一秒做完手上的事来一下我办公室,做不完滚蛋
// 1s 后:做不完,滚蛋
4. new Promise().then().then()...
Эта идея довольно проста, то есть возвращаемое значение функции then является другим экземпляром Promise. Изменено в соответствии со спецификацией следующим образом:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function resolvePromise(promise2, x, resolve, reject) {
// 循环引用报错
if (x === promise2) {
// reject报错
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 防止多次调用
let called;
// x不是null 且x是对象或者函数
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
// A+规定,声明then = x的then方法
let then = x.then;
// 如果then是函数,就默认是promise了
if (typeof then === 'function') {
// 就让then执行 第一个参数是this 后面是成功的回调 和 失败的回调
then.call(x, y => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
}, err => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err); // 失败了就失败了
})
} else {
resolve(x); // 直接成功即可
}
} catch (e) {
// 也属于失败
if (called) return;
called = true;
// 取then出错了那就不要在继续执行了
reject(e);
}
} else {
resolve(x);
}
}
class Promise {
constructor(executor) {
this.state = PENDING
this.value = ''
this.reason = ''
// 成功存放的数组
this.onResolvedCallbacks = [];
// 失败存放法数组
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
// pending->fulfilled 按照成功清单执行
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = reason => {
if (this.state === PENDING) {
this.state = REJECTED
this.reason = reason
// pending->rejected 按照异常清单执行
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected如果不是函数,就忽略onRejected,扔出错误
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};
let promise2 = new Promise((resolve, reject) => {
if (this.state === FULFILLED) {
// 异步解决:
// onRejected返回一个普通的值,失败时如果直接等于 value => value,
// 则会跑到下一个then中的onFulfilled中,
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
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;
}
}
new Promise(resolve => {
console.log(0)
setTimeout(() => resolve(1), 3000)
})
.then(res => {
console.log(res)
return new Promise(resolve => {
console.log(2)
setTimeout(() => {
resolve(3)
}, 3000)
})
})
.then(res => {
console.log(res)
})
5. ловить, разрешать, отклонять, гоняться и все такое
1. catch (специальный метод then)
catch(fn){
return this.then(null,fn)
}
2. разрешить (разрешить значение)
Promise.resolve = val => new Promise(resolve=> resolve(val))
3. отклонить (отклонить значение)
Promise.reject = val => new Promise((resolve,reject)=> reject(val))
4. race
В Promise.race([p1, p2, p3]), в зависимости от того, какой результат окажется быстрее, вернуть этот результат, независимо от того, является ли сам результат состоянием успеха или состоянием отказа.
Promise.race = promises =>
new Promise((resolve, reject) =>
promises.forEach(pro => pro.then(resolve, reject))
)
5. all
Promise.all может обернуть несколько экземпляров Promise в новый экземпляр Promise. При этом возвращаемые значения успеха и неудачи различны, при успехе возвращает массив результатов, а при неудаче возвращает значение первого отклоненного состояния отказа.
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let index = 0;
let result = [];
if (promises.length === 0) {
resolve(result);
} else {
function processValue(i, data) {
result[i] = data;
if (++index === promises.length) {
resolve(result);
}
}
for (let i = 0; i < promises.length; i++) {
//promises[i] 可能是普通值
Promise.resolve(promises[i]).then((data) => {
processValue(i, data);
}, (err) => {
reject(err);
return;
});
}
}
});
}
Эта статья направлена на то, чтобы записать процесс обучения и углубить впечатление, набрав код вручную. Более сложный код выше относится к другим блогам. Видно, что
Реализация исходного кода Promise (полностью соответствует спецификации Promise/A+)