红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 前端学习笔记
  3. 正文

JavaScript 学习笔记 7:Promise

2026年4月10日 3点热度 0人点赞 0条评论

Promise

JS 可以定义一个Promise对象,用于在一段异步调用后通过注册的回调函数获取执行结果:

let promise = new Promise(function (resolve, reject) {
    setTimeout(() => {
        if (Math.random() > 0.5) {
            resolve(42);
        }
        else {
            reject(new Error('失败'));
        }
    }, 1000);
});
​
promise.then(function (value) {
    console.log('success', value); // 42
}, function (error) {
    console.log('fail', error); // Error: 失败
});

Promise的构造函数接收一个函数作为参数,其具有两个参数:

  • resolve,有一个参数的函数,在执行成功后调用

  • reject,有一个参数的函数,执行失败后调用

Promise 构造函数的参数会在 Promise 对象创建后被立即调用。作为执行结构的钩子函数resolve和reject可以通过Promise.then方法注册,它可以接收2个参数,第一个是resolve,第二个是reject。

注册钩子函数更常见的方式是:

promise2.then(function (value) {
    console.log('success', value); // 42
})
.catch(function (error) {
    console.log('fail', error);
})
.finally(function () {
    console.log('finally');
});

链式调用

Promise.then的调用结果是一个新的Promise对象,因此如果要对Promise对象结果进行多次处理,可以通过链式调用的方式:

new Promise(function (resolve, reject) {
    setTimeout(() => {
        let randomNum = Math.random();
        console.log(`${randomNum}`);
        resolve(randomNum);
    }, 1000);
}).then(function (value) {
    let randomNum = Math.random();
    console.log(`${value} + ${randomNum}`)
    return value + randomNum;
}).then(function (value) {
    let randomNum = Math.random();
    console.log(`${value} + ${randomNum}`)
    return value + randomNum;
}).then(function (value) {
    console.log(value);
});
// 0.30704685466788906
// 0.30704685466788906 + 0.6445051295238531
// 0.9515519841917421 + 0.6476301325887244
// 1.5991821167804665

这里的关键是每次.then调用后返回的是新的Promise对象,所以下面的方式是错误的:

let promise3 = new Promise(function (resolve, reject) {
    setTimeout(() => {
        let randomNum = Math.random();
        console.log(`${randomNum}`);
        resolve(randomNum);
    }, 1000);
});
​
promise3.then(function (value) {
    let randomNum = Math.random();
    console.log(`${value} + ${randomNum}`)
    return value + randomNum;
});
​
promise3.then(function (value) {
    let randomNum = Math.random();
    console.log(`${value} + ${randomNum}`)
    return value + randomNum;
});
​
promise3.then(function (value) {
    let randomNum = Math.random();
    console.log(`${value} + ${randomNum}`)
    return value + randomNum;
});
// 0.43215362083224207
// 0.43215362083224207 + 0.5543302400890311
// 0.43215362083224207 + 0.6470879646639638
// 0.43215362083224207 + 0.6314888642633443

如果.then返回的内容不能立即得到,需要异步执行,就不能像前面那样简单的直接return结果,而是需要return一个Promise对象:

new Promise(function (resolve, reject) {
    setTimeout(() => {
        let randomNum = Math.random();
        console.log(`${randomNum}`);
        resolve(randomNum);
    }, 1000);
}).then(function (value) {
    return new Promise(function (resolve, reject) {
        setTimeout(() => {
            let randomNum = Math.random();
            console.log(`${value} + ${randomNum}`)
            resolve(value + randomNum);
        }, 1000);
    });
}).then(function (value) {
    return new Promise(function (resolve, reject) {
        setTimeout(() => {
            let randomNum = Math.random();
            console.log(`${value} + ${randomNum}`)
            resolve(value + randomNum);
        }, 1000);
    });
}).then(function (value) {
    console.log(value);
});
// 0.9100113268839283
// 0.9100113268839283 + 0.017846977573172218
// 0.9278583044571005 + 0.24503169342106612
// 1.1728899978781666

错误处理

链式调用时,可以在调用链的尾部添加一个.catch进行错误捕获,此时链上任意.then抛出错误都会被捕获:

new Promise(function (resolve, reject) {
    resolve(1);
}).then(function (value) {
    value++;
    console.log(value);
    return value;
}).then(function (value) {
    value++;
    console.log(value);
    return value;
}).then(function (value) {
    throw new Error('出错了');
}).catch(function (err) {
    console.log(err);
});
// 2
// 3
// Error: 出错了
//     at D:\workspace\learn-javascript\ch1\src\promise.js:135:11
//     at processTicksAndRejections (node:internal/process/task_queues:96:5)

可以在.catch中重新抛出错误,重新抛出的错误将被链上的下一个.catch捕获并处理:

let promise4 = createPromise();
promise4.then(function (value){
    throw new Error('出错了');
})
.catch(function (err) {
    console.log(err);
    throw new Error('新的错误');
})
.then(function (value) {
    console.log('这里不会被执行');
})
.catch(function (err) {
    console.log(err);
});
// Error: 失败
//     at Timeout._onTimeout (D:\workspace\learn-javascript\ch1\src\promise.js:25:24)
//     at listOnTimeout (node:internal/timers:559:17)
//     at processTimers (node:internal/timers:502:7)
// Error: 新的错误
//     at D:\workspace\learn-javascript\ch1\src\promise.js:151:11

API

Promise.all

如果有一组 Promise 执行,且你需要在所有 Promise 执行成功后统一处理结果,就可以使用Proise.all:

Promise.all([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
        }, 500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    })
]).then(function (numbers) {
    console.log(numbers);
});
// [ 1, 2, 3 ]

示例中的.then中,参数numbers中包含所有 Promise 的处理结果,是一个数组,且其中的顺序与Promise.all参数中的 Promise 顺序是一一对应的。

如果这一组 Promise 中有任意要给报错,则 Promise.all 进入错误处理程序(.catch):

Promise.all([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('Promise 失败'));
        }, 500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    })
]).then(function (numbers) {
    console.log(numbers);
}).catch(function (err) {
    console.log(err);
});
// Error: Promise 失败
//     at Timeout._onTimeout (D:\workspace\learn-javascript\ch1\src\promise.js:195:20)
//     at listOnTimeout (node:internal/timers:559:17)
//     at processTimers (node:internal/timers:502:7)

Promise.allSetteted

Promise.all执行时,一组 Promise 中的任意一个执行失败,都会视为整体失败,会进入异常处理流程:

Promise.all([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('模拟异步执行失败'));
        }, 500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    })
]).then(function (results) {
    console.log('这里不会生效');
}).catch(function (err) {
    console.log('异常捕获', err);
});
// 异常捕获 Error: 模拟异步执行失败
//     at Timeout._onTimeout (D:\workspace\learn-javascript\ch1\src\promise2.js:10:24)
//     at listOnTimeout (node:internal/timers:559:17)
//     at processTimers (node:internal/timers:502:7)

如果你运行部分执行失败,在这种情况下依然处理正常执行后的结果,可以使用Promise.allSettled:

Promise.allSettled([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('模拟异步执行失败'));
        }, 500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    })
])
.then(function (results) {
    console.log('results', results);
    results.forEach((result, num)=>{
        if(result.status === 'fulfilled'){
            console.log(`第${num}个结果成功`, result.value);
        }else{
            console.log(`第${num}个结果失败`, result.reason.message);
        }
    });
});
// 第0个结果成功 1
// 第1个结果失败 模拟异步执行失败
// 第2个结果成功 3

Promise.race

Promise.race会处理第一个执行完成的任务:

Promise.race([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
        }, 500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('模拟异步执行失败'));
        }, 3000);
    })
]).then(function (result) {
    console.log('第一个执行成功的任务', result);
}).catch(function (err) {
    console.log('失败', err);
});
// 第一个执行成功的任务 2

如果第一个执行完成的任务是失败的,则进入错误处理程序:

Promise.race([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('模拟异步执行失败'));
        }, 500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    })
])
.then(function (result) {
    console.log('第一个执行成功的任务', result);
}).catch(function (err) {
    console.log('失败', err);
});
// 失败 Error: 模拟异步执行失败

Promise.any

如果仅需要第一个执行成功的结果,可以使用Promise.any:

Promise.any([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('模拟异步执行失败'));
        }, 500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    })
]).then(function (result) {
    console.log('第一个执行成功的任务', result);
});
// 第一个执行成功的任务 1

如果所有任务都执行失败:

Promise.any([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('任务1执行失败'));
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('任务2执行失败'));
        }, 500);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('任务3执行失败'));
        }, 3000);
    })
])
.then(function (result) {
    console.log('第一个执行成功的任务', result);
}).catch(function (errors) {
    if (errors instanceof AggregateError) {
        console.log('所有任务都执行失败');
        for (const [index, error] of errors.errors.entries()) {
            console.log(`任务${index}执行失败`, error.message);
        }
    }
});
// 所有任务都执行失败
// 任务0执行失败 任务1执行失败
// 任务1执行失败 任务2执行失败
// 任务2执行失败 任务3执行失败

Promise.resolve/reject

Promise.resolve可以将一个值包装成执行成功的 Promise:

Promise.resolve(1).then(function (result) {
    console.log('resolve', result);
});
// resolve 1

效果相当于:

new Promise(function (resolve) {
    resolve(1);
}).then(function (result) {
    console.log('resolve', result);
});
// resolve 1

Promise.reject可以将一个错误包装成 Promise:

Promise.reject(new Error('模拟异步执行失败'))
    .catch(function (err) {
    console.log('失败', err);
});
// 失败 Error: 模拟异步执行失败

效果相当于:

new Promise(function (resolve, reject) {
    reject(new Error('模拟异步执行失败'));
}).catch(function (err) {
    console.log('失败', err);
});
// 失败 Error: 模拟异步执行失败

Promisefication

通常,如果需要异步执行一段任务,并在执行完毕后处理执行结果,会通过回调解决:

function asyncTask(data, callback){
    setTimeout(() => {
        let result = Math.random();
        if(result > 0.5){
            callback(new Error('模拟异步执行失败'));
        }
        else{
            callback(null, result);
        }
    }, 1000);
}
asyncTask(1, (error, result)=>{
    if(error){
        console.log('失败', error);
    }
    else{
        console.log('成功', result);
    }
});

JS 中的回调函数通常约定第一个参数表示异常,第二个参数表示正常的返回结果。

它可以正常工作,但我们已经学过 Promise,如果能让异步任务执行返回一个 Promise 对象,代码结构会更友好:

function asyncTask(data){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            let result = Math.random();
            if(result > 0.5){
                reject(new Error('模拟异步执行失败'));
            }
            else{
                resolve(result);
            }
        }, 1000);
    });
}
​
asyncTask(1)
    .then(function (result) {
    console.log('成功', result);
})
    .catch(function (error) {
    console.log('失败', error);
});

如果asyncTask是新编写的函数,这么做没什么问题,但如果是一个老旧的第三方库中的使用回调方式的函数,要让其返回一个 Promise 就需要借助工具函数进行包装和转换:

function asyncTask(data, callback){
    setTimeout(() => {
        let result = Math.random();
        if(result > 0.5){
            callback(new Error('模拟异步执行失败'));
        }
        else{
            callback(null, result);
        }
    }, 1000);
}
​
function promisefication(asyncTask, data) {
    return new Promise((resolve, reject) => {
        asyncTask(data, (error, result) => {
            if (error) {
                reject(error);
            }
            else {
                resolve(result);
            }
        });
    });
}
​
promisefication(asyncTask, 1)
    .then(function (result) {
    console.log('成功', result);
})
    .catch(function (error) {
    console.log('失败', error);
});

工具函数可以正确转换,返回一个返回 Promise 对象的函数,但不够泛用性,只能处理接收一个参数的函数,升级为可以处理任意个参数的函数:

function asyncTask(data, callback){
    setTimeout(() => {
        let result = Math.random();
        if(result > 0.5){
            callback(new Error('模拟异步执行失败'));
        }
        else{
            callback(null, result);
        }
    }, 1000);
}
​
function promisefication(fn){
    return function (...args){
        return new Promise(function (resolve, reject) {
            fn(...args, function (err, result) {
                if(err){
                    reject(err);
                }
                else{
                    resolve(result);
                }
            });
        });
    }
}
​
promisefication(asyncTask)(1)
    .then(function (result) { 
    console.log(result);
})
    .catch(function (err) { 
    console.log(err);
});

实际上通常并不需要我们自己编写这类工具函数,比如 Node.js 就提供一个:

const { promisify } = require('util');
function asyncTask(data, callback) {
    setTimeout(() => {
        let result = Math.random();
        if (result > 0.5) {
            callback(new Error('模拟异步执行失败'));
        }
        else {
            callback(null, result);
        }
    }, 1000);
}
promisify(asyncTask)(1)
    .then(result => {
    console.log('成功', result);
})
    .catch(err => {
    console.log('失败', err);
});

Async

函数可以用一个async关键字修饰:

async function asyncFunc(){
    return 1;
}
​
asyncFunc().then(function(value){ 
    console.log(value); 
});
// 1

这样的函数必然会返回一个Promise对象,如果没有显式返回,会被自动包装成 Promise对象,上面的示例等价于:

async function asyncFunc(){
    return Promise.resolve(1);
}
​
asyncFunc().then(function(value){ 
    console.log(value); 
});
// 1

await

await等待Promise对象产生结果,并返回其结果:

let promise = new Promise(function (resolve, reject) {
    setTimeout(function () {
        let result = Math.random();
        if (result > 0.5) {
            resolve(result);
        }
        else {
            reject(result);
        }
    }, 1000);
});
try {
    let result = await promise;
    console.log('success', result);
}
catch (e) {
    console.log('fail', e);
}

程序会在await的部分暂时终止运行,以的等待Promise对象产生结果。

这种编写方式可以减少.then和.catch的编写,将异步代码变得更像是同步代码。

类方法也可以使用async和await:

class User{
    constructor(name) {
        this.name = name;
    }
    async getName(){
        return this.name;
    }
}
​
let user = new User('Mike');
let name = await user.getName();
console.log(name);

The End.

本文的完整示例可以从这里获取。

参考资料

  • 现代 JavaScript 教程

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2026年4月10日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号