Last active
January 24, 2024 12:09
-
-
Save teal-front/0284e1c2bdcb6623f7110d9c60fc1469 to your computer and use it in GitHub Desktop.
promise
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 限制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); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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'); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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++) | |
} | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// catch返回一个新的Promise对象 | |
Promise.reject(44).catch(() => 33) // => Promise<33> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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