Skip to content

Instantly share code, notes, and snippets.

@teal-front
Last active January 24, 2024 12:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save teal-front/0284e1c2bdcb6623f7110d9c60fc1469 to your computer and use it in GitHub Desktop.
Save teal-front/0284e1c2bdcb6623f7110d9c60fc1469 to your computer and use it in GitHub Desktop.
promise
const assert = require('assert').strict
// 对于promise execute函数里的错误会被rejected,并不会最终抛出异常
// 那allSettled什么时候会是rejected状态呢?
Object.assign(Promise, {
myAllSettled(promises) {
return new Promise((resolve, reject) => {
const ans = new Array(promises.length)
let count = 0
for (let i = 0; i < promises.length; i++) {
const promise = promises[i]
promise.then((val) => {
ans.splice(i, 0, {
status: 'fulfilled',
value: val
})
}).catch(reason => {
ans.splice(i, 0, {
status: 'rejected',
reason
})
}).finally(() => {
if (++count === promises.length) resolve(ans)
})
}
})
}
})
const promise1 = () => new Promise((resolve, reject) => {
// throw new Error('')
a()
})
Promise.myAllSettled([promise1()]).then(val => {
console.log('val', val);
assert.deepEqual(val, (err) => {
return true
})
}).catch(e => {
// never arrive?
console.log('error', e);
})
// mdn https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally
// Promise.resolve(44).finally(() => 33)返回44,Promise.reject(44).finally(() => 33)也是返回rejected为44的Promise
// Promise.resolve(44).finally(() => Promise.reject(33))会返回onFinally回调自身返回的33rejected promise,Promise.reject(44).finally(() => Promise.reject(33))也是同理
// https://github.com/matthew-andrews/Promise.prototype.finally/blob/master/finally.js
Promise.prototype['finally'] = function finallyPolyfill(callback) {
var constructor = this.constructor;
return this.then(function(value) {
return constructor.resolve(callback()).then(function() {
return value;
});
}, function(reason) {
return constructor.resolve(callback()).then(function() {
// throw !
throw reason;
});
});
};
// use async
Promise.prototype.finally = function(callback) {
return this.then(async function(value) {
await callback()
return value
}, async function(reason) {
await callback()
// throw !
throw reason
})
}
const log = console.log.bind(console)
const promiseUnknown = Promise.reject('fulfilled-1')
Promise.race([
// 要比较的promise要放在最前面
promiseUnknown,
Promise.resolve('fulfilled'),
]).then(value => {
if (value === 'fulfilled') {
log('fulfilled')
} else {
log('pending')
}
}).catch(() => log('rejected'))
/**
* 限制promise.all的并发执行数,下面的方案是Promise.race+Promise.all
* 另一种只使用了Promise.all的方案,还需要用map来保存array与返回结果,然后按顺序返回。 https://juejin.cn/post/6844904115487637517
*/
// https://github.com/rxaviers/async-pool/blob/master/lib/es7.js#L26
/**
* @param {number} poolLimit 限制数
* @param {any[]} array iteratorFn的执行参数
* @param {(item, array) => any} iteratorFn 可执行函数,可返回promise/async function
*/
export async function asyncPool<V = unknown, P = unknown>(
poolLimit: number,
array: P[],
iteratorFn: (...item: unknown[]) => Promise<V>,
): Promise<(PromiseSettledResult<V> & { params: P })[]> {
const ret = [];
const executing = [];
for (const item of array) {
const p = Promise.resolve().then(async () => {
// 添加了对promise的获取
try {
const paramaters = Array.isArray(item) ? item : [item];
const result = await iteratorFn(...paramaters);
return {
status: 'fulfilled',
value: result,
params: item,
};
} catch (error) {
return {
status: 'rejected',
reason: error,
// 失败时返回对应的参数
params: item,
};
}
});
ret.push(p);
if (poolLimit <= array.length) {
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);
if (executing.length >= poolLimit) {
await Promise.race(executing);
}
}
}
return Promise.all(ret);
}
// 支持重试的async pool
// todo
/*
* ES9的async-pool版本,需结合for await一起使用
好像缺少了错误捕获
https://github.com/rxaviers/async-pool
*/
async function* asyncPool(concurrency, iterable, iteratorFn) {
const executing = new Set();
async function consume() {
const [promise, value] = await Promise.race(executing);
executing.delete(promise);
return value;
}
for (const item of iterable) {
// Wrap iteratorFn() in an async fn to ensure we get a promise.
// Then expose such promise, so it's possible to later reference and
// remove it from the executing pool.
const promise = (async () => await iteratorFn(item, iterable))().then(
value => [promise, value]
);
executing.add(promise);
if (executing.size >= concurrency) {
yield await consume();
}
}
while (executing.size) {
yield await consume();
}
}
(async () => {
const timeout = ms => new Promise(resolve => setTimeout(() => resolve(ms), ms));
for await (const ms of asyncPool(2, [1000, 5000, 3000, 2000], timeout)) {
console.log(ms);
}
})();
// 运行事件触发器来实现Promise列表中,有promise状态变更时,触发事件
function runEventPromiseEmitter() {
// 导入事件触发器模块
const EventEmitter = require('events');
// 创建PromiseList类,继承事件触发器
class PromiseList extends EventEmitter {
constructor() {
super();
this.promises = [];
}
// 添加Promise到列表中
addPromise(promise) {
this.promises.push(promise);
// 监听Promise状态变化,并触发通知
promise.then(
(value) => {
this.emit('promiseResolved', value);
},
(reason) => {
this.emit('promiseRejected', reason);
}
);
}
}
// 创建PromiseList实例
const promiseList = new PromiseList();
// 添加Promise到列表中
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise 1 resolved'), 1000);
});
promiseList.addPromise(promise1);
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject('Promise 2 rejected'), 2000);
});
promiseList.addPromise(promise2);
// 监听Promise状态变化的通知
promiseList.on('promiseResolved', (value) => {
console.log('Promise resolved:', value);
});
promiseList.on('promiseRejected', (reason) => {
console.log('Promise rejected:', reason);
});
}
// https://juejin.cn/post/6844903988584775693#heading-1
async function async1() {
// 此时执行完awit并不先把await后面的代码注册到微任务队列中去,而是执行完await之后,
// 直接跳出async1函数,执行其他代码。
// 其他代码执行完毕后,需要回到async1函数去执行剩下的代码
// 等async2 fullfilled时,再将await后面的包装成Promise.then,所以晚于promise1
await async2();
// 函数协程获得控制权,接着执行
console.log('async1 end');
}
async function async2() {
// 如果await 后面直接跟的为一个变量,
// 比如:await 1;这种情况的话相当于直接把await后面的代码注册为一个微任务,
// 可以简单理解为promise.then(await下面的代码)。
// 然后跳出async1函数,执行其他代码
// 当遇到promise函数的时候,会注册promise.then()函数到微任务队列,
// 注意此时微任务队列里面已经存在await后面的微任务。
// 所以这种情况会先执行await后面的代码(async2 end),
// 再执行async1函数后面注册的微任务代码(promise1)。
await async3();
console.log('async2 end');
}
async function async3() {
setTimeout(() => {
console.log('async3 setTimeout');
Promise.resolve().then(() => {
console.log('async3 promise')
});
}, 0)
}
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
resolve();
}).then(function() {
console.log('promise1');
});
// https://brunoscopelliti.com/blog/lets-write-a-promise-polyfill/
function isEmptyIterable(iterable) {
for(const _ of iterable) {
return false
}
return true
}
Promise.all = function (iterable) {
return new Promise((resolve, reject) => {
if (!(iterable && typeof iterable[Symbol.iterator] === 'function')) throw new TypeError('iterable must')
if (isEmptyIterable(iterable)) return resolve([])
let count = 0
let n = 0
const list = []
for(const promise of iterable) {
((promise, i) => {
Promise.resolve(promise).then((data) => {
list[i] = data
count++
if (count === n) {
resolve(list)
}
}, reject)
})(promise, n++)
}
})
}
// catch返回一个新的Promise对象
Promise.reject(44).catch(() => 33) // => Promise<33>
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
promises.forEach(promise => {
Promise.resolve(promise).then(resolve, reject)
})
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment