1.高阶函数 #

高阶函数(Higher-order functions)是一个在JavaScript中非常重要且强大的概念。它们允许我们编写更简洁、更灵活和更易维护的代码。

在JavaScript中,函数是一等公民(first-class citizens),这意味着我们可以将函数作为变量存储、将它们作为参数传递给其他函数,或者从其他函数返回它们。高阶函数就是利用这一特性,接受一个或多个函数作为参数,或返回一个函数的函数。

1. 高阶函数接受一个或多个函数作为参数

常见的高阶函数包括 mapfilterreduce。这些高阶函数都接受一个函数作为参数,并对数组的每个元素应用这个函数。

例如,map 函数将一个函数应用于数组中的每个元素,并返回一个新数组:

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

filter 函数接受一个函数作为参数,并返回一个包含那些使该函数返回真值的元素的新数组:

const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]

2. 高阶函数可以返回一个函数

返回函数的高阶函数允许我们创建具有特定行为的新函数。这是函数式编程中的一个常见模式,被称为函数柯里化(currying)或部分应用(partial application)。

例如,下面是一个简单的高阶函数,它接受一个乘数,并返回一个新的函数,该函数将其输入乘以该乘数:

function multiplier(factor) {
  return function (x) {
    return x * factor;
  }
}

const timesTwo = multiplier(2);
console.log(timesTwo(5)); // 10

高阶函数的使用可以使代码更加抽象、更具可重用性,使我们能够将逻辑分解为更小、更容易理解的部分。在JavaScript和其他支持函数式编程的语言中,高阶函数是一个非常有用的工具。

2.函数柯里化 #

函数柯里化(Function Currying)是函数式编程中的一个技术,它可以将接受多个参数的函数转换为一系列使用一个参数的函数。柯里化的主要目的是创建可以轻易地为常见任务进行参数化的函数版本。

为什么要使用柯里化?

  1. 参数重用:通过固定一部分参数,我们可以创建具有少量参数的新函数。
  2. 延迟计算:能够创建并返回函数,直到我们得到所有参数后再进行实际的计算。
  3. 提高可读性和可维护性:使代码更简洁、更具描述性。

如何实现柯里化?

考虑一个简单的两数相加函数:

function add(a, b) {
    return a + b;
}

柯里化版本:

function curriedAdd(a) {
    return function(b) {
        return a + b;
    }
}
const add5 = curriedAdd(5);
console.log(add5(3)); // 输出 8

这个例子展示了基本的柯里化,但如果我们想柯里化一个接受多个参数的函数呢?

通用的柯里化函数:

function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return function (...args2) {
                return curried.apply(this, args.concat(args2));
            }
        }
    };
}

使用上面的 curry 函数,我们可以将任何函数柯里化:

const curriedAdd = curry(add);
console.log(curriedAdd(5)(3)); // 输出 8

注意事项:

3.异步编程 #

在 JavaScript 中,异步编程是一种常见的编程范式,特别是在 I/O 密集型任务(如文件读写或网络请求)中。Node.js 提供了非阻塞的 I/O 操作,这意味着主线程不会等待 I/O 操作完成,而是继续执行后续代码。当 I/O 操作完成时,会执行相应的回调函数。

示例:使用 fs.readFile 读取文件 #

Node.js 的 fs 模块提供了 readFile 方法,该方法用于异步读取文件内容。它接受文件路径和回调函数作为参数。

const fs = require('fs');

fs.readFile('path/to/file.txt', 'utf8', (err, data) => {
    if (err) {
        console.error('Error reading the file:', err);
        return;
    }
    console.log('File content:', data);
});

console.log('Reading file...');

上述代码中,fs.readFile 是一个异步操作。当调用它时,主线程不会等待文件读取完成,而是继续执行 console.log('Reading file...')。一旦文件读取完成,提供给 readFile 的回调函数会被调用。

为什么使用异步编程? #

异步编程的挑战 #

尽管异步编程有很多好处,但它也带来了一些挑战:

  1. 回调地狱(Callback Hell):多个嵌套回调会导致代码难以阅读和维护。这通常被称为“回调地狱”或“金字塔死亡”。
doSomething(result => {
    doSomethingElse(result, newResult => {
        doYetAnotherThing(newResult, finalResult => {
            // ... and so on ...
        });
    });
});
  1. 错误处理:传统的 try/catch 错误处理在异步回调中不起作用。你必须依赖回调函数的 err 参数来捕获和处理错误。

为了解决这些挑战,现代 JavaScript 引入了 Promises、async/await 等特性,使异步编程更加直观和易于管理。但回调仍然是 Node.js 和许多库中的常见模式,因此了解其工作原理是很有用的。

4. Promise #

2.1 创建一个 Promise #

const promise = new Promise((resolve, reject) => {
    //resolve('value');
    //reject('reason');
});
console.dir(promise);

2.2 实例方法 #

属性/方法 描述
then 返回一个新的Promise,并处理Promise的完成(resolved)状态。
catch 返回一个新的Promise,并处理Promise的拒绝(rejected)状态。
finally 返回一个新的Promise,在Promise完成或拒绝后执行一段代码。

2.2.1 then #

2.2.1.1 then #
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    if (Math.random() > 0.5) {
      resolve('成功');
    } else {
      reject('失败');
    }
  }, 1000);
});
promise.then(
  (result) => {
    console.log(result);
  },
  (reason) => {
    console.error(reason);
  }
);

Promise.js

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 = [];
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn=>fn());
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                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){
            this.onResolvedCallbacks.push(()=>{
                onFulfilled(this.value);
            });
            this.onRejectedCallbacks.push(()=>{
                onRejected(this.reason);
            });
        }
    }
}
module.exports = Promise;
2.2.1.2 可选参数 #
const promise = new Promise((resolve, reject) => {
    resolve('成功');
    reject('失败')
});
promise.then().then().then((result) => {
    console.log(result);
},(reason) => {
        console.error(reason);
    });

Promise.js

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 = [];
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
+   then(onFulfilled = value => value, onRejected = reason => { throw reason }) {
+       let promise2 = new Promise((resolve, reject) => {
+           if (this.status === FULFILLED) {
+               try {
+                   const x = onFulfilled(this.value);
+                   resolve(x);
+               } catch (error) {
+                   reject(error)
+               }
+           }
+           if (this.status === REJECTED) {
+               try {
+                   const x = onRejected(this.reason);
+                   resolve(x);
+               } catch (error) {
+                   reject(error)
+               }
+           }
+           if (this.status === PENDING) {
+               this.onResolvedCallbacks.push(() => {
+                   try {
+                       const x = onFulfilled(this.value);
+                       resolve(x);
+                   } catch (error) {
+                       reject(error)
+                   }
+               });
+               this.onRejectedCallbacks.push(() => {
+                   try {
+                       const x = onRejected(this.reason);
+                       resolve(x);
+                   } catch (error) {
+                       reject(error)
+                   }
+               });
+           }
+       })
+       return promise2;
    }
}
module.exports = Promise;
2.2.1.3 onFulfilled #
const promise = new Promise((resolve, reject) => {
    resolve('成功');
    resolve('成功');
});
promise.then((value) => {
    console.log(value);
});
2.2.1.4 onRejected #
const promise = new Promise((resolve, reject) => {
    reject('失败');
    reject('失败');
});
promise.then(undefined, (error) => {
    console.log(error);
});
2.2.1.5 异步 #
const Promise = require('./Promise');
const promise = new Promise((resolve, reject) => {
    resolve('成功');
});
promise.then((value) => {
    console.log(value);
});
console.log('用户代码执行完毕');

Promise.js

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 = [];
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
    then(onFulfilled = value => value, onRejected = reason => { throw reason }) {
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
+               queueMicrotask(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolve(x);
                    } catch (error) {
                        reject(error)
                    }
+               });
            }
            if (this.status === REJECTED) {
+               queueMicrotask(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolve(x);
                    } catch (error) {
                        reject(error)
                    }
+               });
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
+                   queueMicrotask(() => {
                        try {
                            const x = onFulfilled(this.value);
                            resolve(x);
                        } catch (error) {
                            reject(error)
                        }
+                   });
                });
                this.onRejectedCallbacks.push(() => {
+                   queueMicrotask(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolve(x);
                        } catch (error) {
                            reject(error)
                        }
+                   });
                });
            }
        })
        return promise2;
    }
}
module.exports = Promise;
2.2.1.6 函数调用 #
const promise = new Promise((resolve, reject) => {
    resolve('成功');
});
promise.then(function (value) {
    console.log(this);
});
2.2.1.7 多次调用 #
const promise = new Promise((resolve, reject) => {
    resolve('成功');
});
promise.then(function (value) {
    console.log(1, value);
});
promise.then(function (value) {
    console.log(2, value);
});
2.2.1.8 返回promise #

2.2.7.3

const promise = new Promise((resolve, reject) => {
    resolve('成功');
});
let promise2 = promise.then(function (value) {
    console.log(value);
    throw new Error('then error');
});
promise2.then(undefined, function (err) {
    console.log(err);
});

2.2.7.4 2.2.7.5

const promise = new Promise((resolve, reject) => {
    //resolve('成功');
    reject('失败');
});
let promise2 = promise.then();
promise2.then(function (value) {
    console.log(value);
}, function (err) {
    console.log(err);
});
2.2.1.9 Promise Resolution Procedure #
//const Promise = require('./Promise');
const promise = new Promise((resolve, reject) => {
    resolve('成功');
});
let promise2 = promise.then(function (value) {
    return promise2;
});
promise2.then(
  (value) => { console.log(value) }, 
  (reason) => { console.log(reason) });

Promise.js

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
+function resolvePromise(x, promise2, resolve, reject) {
+    if (x === promise2) {
+        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
+    }
+}
class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
    then(onFulfilled = value => value, onRejected = reason => { throw reason }) {
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                queueMicrotask(() => {
                    try {
                        const x = onFulfilled(this.value);
+                       resolvePromise(x, promise2, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if (this.status === REJECTED) {
                queueMicrotask(() => {
                    try {
                        const x = onRejected(this.reason);
+                       resolvePromise(x, promise2, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onFulfilled(this.value);
+                           resolvePromise(x, promise2, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    });
                });
                this.onRejectedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onRejected(this.reason);
+                           resolvePromise(x, promise2, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    });
                });
            }
        })
        return promise2;
    }
}
module.exports = Promise;
const Promise = require('./Promise');
const promise = new Promise((resolve, reject) => {
    resolve('成功');
});
let promise2 = promise.then(function (value) {
    return new Promise((resolve, reject) => {
        resolve('success');
        //reject('error');
    });;
});
promise2.then(function (value) {
    console.log(value);
}, function (reason) {
    console.log(reason);
}
);

Promise.js

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
function resolvePromise(x, promise2, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
+   if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) {
+       let then = x.then;
+       if (typeof then === 'function') {
+           then.call(x, y => {
+               resolvePromise(y, promise2, resolve, reject)
+           }, r => {
+               reject(r);
+           });
+       } else {
+           resolve(x);
+       }
+   } else {
+       resolve(x);
+   }
}
class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
    then(onFulfilled = value => value, onRejected = reason => { throw reason }) {
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                queueMicrotask(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(x, promise2, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if (this.status === REJECTED) {
                queueMicrotask(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolvePromise(x, promise2, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onFulfilled(this.value);
                            resolvePromise(x, promise2, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    });
                });
                this.onRejectedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolvePromise(x, promise2, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    });
                });
            }
        })
        return promise2;
    }
}
module.exports = Promise;
const Promise = require('./Promise');
const promise = new Promise((resolve, reject) => {
    resolve('成功');
});

let promise2 = promise.then(function (value) {
    let obj = {};
    Object.defineProperty(obj, 'then', {
        get() {
            throw new Error();
        }
    });
    return obj;
});
promise2.then(function (value) {
    console.log(value);
}, function (reason) {
    console.log(reason);
}
);

Promise.js

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
function resolvePromise(x, promise2, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) {
+       try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, y => {
                    resolvePromise(y, promise2, resolve, reject)
                }, r => {
                    reject(r);
                });
            } else {
                resolve(x);
            }
+       }catch(error){
+           reject(error);
+       }
    } else {
        resolve(x);
    }
}
class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
    then(onFulfilled = value => value, onRejected = reason => { throw reason }) {
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                queueMicrotask(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(x, promise2, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if (this.status === REJECTED) {
                queueMicrotask(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolvePromise(x, promise2, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onFulfilled(this.value);
                            resolvePromise(x, promise2, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    });
                });
                this.onRejectedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolvePromise(x, promise2, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    });
                });
            }
        })
        return promise2;
    }
}
module.exports = Promise;
const Promise = require('./Promise');
const promise = new Promise((resolve, reject) => {
    resolve('成功');
});
let promise2 = promise.then(function (value) {
    let obj = {
        then(resolvePromise, rejectPromise) {
            //resolvePromise('成功2');
            rejectPromise('失败2');
            throw new Error('失败3');
        }
    };
    return obj;
});
promise2.then(function (value) {
    console.log(value);
},function (reason) {
    console.log(reason);
});
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
function resolvePromise(x, promise2, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) {
+       let called = false
        try {
            let then = x.then;
            if (typeof then === 'function') {
                then.call(x, y => {
+                   if (called) return
+                   called = true
                    resolvePromise(y, promise2, resolve, reject)
                }, r => {
+                   if (called) return
+                   called = true
                    reject(r);
                });
            } else {
                resolve(x);
            }
        }catch(error){
+           if (called) return
+           called = true
            reject(error);
        }
    } else {
        resolve(x);
    }
}
class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
            if (this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }
    then(onFulfilled = value => value, onRejected = reason => { throw reason }) {
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                queueMicrotask(() => {
                    try {
                        const x = onFulfilled(this.value);
                        resolvePromise(x, promise2, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if (this.status === REJECTED) {
                queueMicrotask(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolvePromise(x, promise2, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                });
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onFulfilled(this.value);
                            resolvePromise(x, promise2, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    });
                });
                this.onRejectedCallbacks.push(() => {
                    queueMicrotask(() => {
                        try {
                            const x = onRejected(this.reason);
                            resolvePromise(x, promise2, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    });
                });
            }
        })
        return promise2;
    }
}
module.exports = Promise;
const promise = new Promise((resolve, reject) => {
    resolve('成功');
});

let promise2 = promise.then(function (value) {
    let obj = { name: 'zhufeng' };
    Object.defineProperty(obj, 'then', {
        get() {
            return 'then string'
        }
    });
    return obj;
});
promise2.then(function (value) {
    console.log(value);
}, function (reason) {
    console.log(reason);
}
);
const promise = new Promise((resolve, reject) => {
    resolve('成功');
});

let promise2 = promise.then(function (value) {
    return [1, 2, 3];
});
promise2.then(function (value) {
    console.log(value);
}, function (reason) {
    console.log(reason);
}
);

2.2.2 catch #

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('出错了');
    }, 1000);
});
promise.catch((reason) => {
    console.error(reason);
});
+   catch(onRejected) {
+       return this.then(undefined, onRejected);
+   }

2.2.3 finally #

const Promise = require('./Promise');
const promise = new Promise((resolve, reject) => {
    //resolve('成功');
    reject('reason');
});
promise
    .finally(() => {
        console.log('无论如何都会执行');
    });
    finally(callback) {
        return this.then(
            value => new Promise((resolve, reject) => {
                queueMicrotask(() => {
                    try {
                        callback();
                        resolve(value);
                    } catch (error) {
                        reject(error);
                    }
                });
            }),
            reason => new Promise((resolve, reject) => {
                queueMicrotask(() => {
                    try {
                        callback();
                        reject(reason);
                    } catch (error) {
                        reject(error);
                    }
                });
            })
        );
    }

2.2.4 链式调用 #

const Promise = require('./Promise');
const fs = require('fs');

function readFileAsync(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf8', (err, data) => {
            if (err) reject(err);
            else resolve(data);
        });
    });
}

readFileAsync("name.txt")
    .then(nameData => {
        console.log(nameData);
        return readFileAsync("age.txt");
    })
    .then(ageData => {
        console.log(ageData);
    })
    .catch(error => {
        console.error("An error occurred:", error);
    });

2.3 静态方法 #

静态方法 描述
Promise.all 返回一个 Promise,该 Promise 在所有给定的 Promise 都已解决时解决,并且返回所有 Promise 的解决值数组,顺序与传入的 Promise 数组顺序一致。如果传入的任何 Promise 被拒绝,则返回的 Promise 将立即被拒绝,并且会传递拒绝的原因。
Promise.race 返回一个 Promise,该 Promise 在给定的 Promise 数组中的任何一个 Promise 解决或拒绝时解决或拒绝。返回的 Promise 采用第一个解决或拒绝的 Promise 的值或原因。
Promise.resolve 返回一个已解决的 Promise 对象,解析结果由传入的值决定。如果传入的是一个 Promise 对象,则返回该对象本身。如果传入的是一个 thenable 对象(即具有 then 方法的对象),则返回的 Promise 将采用该 thenable 对象的状态。否则,返回的 Promise 将以传入的值为解决值。
Promise.reject 返回一个拒绝的 Promise 对象,拒绝原因由传入的值决定。
Promise.allSettled 返回一个 Promise,该 Promise 在所有给定的 Promise 都已解决或拒绝后解决,并返回一个包含所有 Promise 结果的数组,每个结果是一个对象,包含该 Promise 的状态和值或原因。
Promise.any 返回一个 Promise,该 Promise 在给定的 Promise 数组中的任何一个 Promise 解决时解决。如果所有 Promise 都被拒绝,则返回的 Promise 将被拒绝,并且会传递所有 Promise 的拒绝原因数组。

2.3.1 Promise.resolve #

Promise.resolve(value);
Promise.resolve('Hello')
    .then(value => {
        console.log(value); // 'Hello'
        return 42;
    })

Promise.js

+static resolve(value) {
+    return new Promise((resolve) => {
+        resolve(value);
+    });
+}

2.3.2 Promise.reject #

Promise.reject(reason);
Promise.reject(new Error('Something went wrong'))
    .catch(error => {
        console.error(error.message); // 'Something went wrong'
    });

Promise.js

+   static reject(reason) {
+       return new Promise((_, reject) => {
+           reject(reason);
+       });
+   }

2.3.3 Promise.all #

Promise.all(iterable);
const promise1 = Promise.resolve('Hello');
const promise2 = 42;
const promise3 = new Promise((resolve) => {
    setTimeout(resolve, 1000, 'World');
});

Promise.all([promise1, promise2, promise3])
    .then(values => {
        console.log(values); // ['Hello', 42, 'World']
    })
    .catch(error => {
        console.error(error);
    });

Promise.js

    const resolve = (value) => {
+       if (value instanceof Promise) {
+           return value.then(resolve, reject)
+       }
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
            this.onResolvedCallbacks.forEach(fn => fn());
        }
    }
+   static all(promises) {
+       return new Promise((resolve, reject) => {
+           let results = [];
+           let completed = 0;
+           promises.forEach((promise, index) => {
+               Promise.resolve(promise).then(value => {
+                   results[index] = value;
+                   completed++;
+                   if (completed === promises.length) {
+                       resolve(results);
+                   }
+               }, reason => {
+                   reject(reason);
+               });
+           });
+       });
+   }

2.3.4 Promise.race #

const promise1 = new Promise(resolve => setTimeout(resolve, 1000, 'Promise 1 resolved'));
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 500, 'Promise 2 rejected'));
const promise3 = new Promise(resolve => setTimeout(resolve, 1500, 'Promise 3 resolved'));

Promise.race([promise1, promise2, promise3])
    .then(result => {
        console.log(result); // 'Promise 2 rejected'
    })
    .catch(error => {
        console.error(error); // 'Promise 2 rejected'
    });

Proimse.js

+    static race(promises) {
+        return new Promise((resolve, reject) => {
+            promises.forEach(promise => {
+                Promise.resolve(promise).then(value => {
+                    resolve(value);
+                }, reason => {
+                    reject(reason);
+                });
+            });
+        });
+    }

2.3.6 Promise.any #

Promise.any(iterable);
const promise1 = new Promise(resolve => setTimeout(resolve, 2000, 'Promise 1 resolved'));
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 1000, 'Promise 2 rejected'));
const promise3 = new Promise(resolve => setTimeout(resolve, 1500, 'Promise 3 resolved'));

Promise.any([promise1, promise2, promise3])
    .then(result => {
        console.log(result); // 'Promise 3 resolved'
    })
    .catch(errors => {
        console.error(errors); // [AggregateError: All promises were rejected]
    });

Promise.js

+   static any(promises) {
+       return new Promise((resolve, reject) => {
+           let completed = 0;
+           let reasons = [];
+           promises.forEach((promise, index) => {
+               Promise.resolve(promise).then(value => {
+                   resolve(value);
+               }, reason => {
+                   reasons[index] = reason;
+                   completed++;
+                   if (completed === promises.length) {
+                       reject(new AggregateError(reasons, "All promises were rejected"));
+                   }
+               });
+           });
+       });
+   }

2.3.5 Promise.allSettled #

Promise.allSettled(iterable);
const promise1 = Promise.resolve('Resolved');
const promise2 = Promise.reject('Rejected');
const promise3 = new Promise(resolve => setTimeout(resolve, 1000, 'Delayed Resolved'));

Promise.allSettled([promise1, promise2, promise3])
    .then(results => {
        console.log(results);
        /* [
            { status: 'fulfilled', value: 'Resolved' },
            { status: 'rejected', reason: 'Rejected' },
            { status: 'fulfilled', value: 'Delayed Resolved' }
        ] */
    });

Promise.js

+   static allSettled(promises) {
+       return new Promise(resolve => {
+           let results = [];
+           let completed = 0;
+           promises.forEach((promise, index) => {
+               Promise.resolve(promise).then(value => {
+                   results[index] = { status: 'fulfilled', value };
+                   completed++;
+                   if (completed === promises.length) {
+                       resolve(results);
+                   }
+               }, reason => {
+                   results[index] = { status: 'rejected', reason };
+                   completed++;
+                   if (completed === promises.length) {
+                       resolve(results);
+                   }
+               });
+           });
+       });
+   }