本文详解JavaScript中Promise对象的三种状态(Pending、Resolved、Rejected)及其转换机制,解析构造函数、then、catch、all及race方法的实际用法。重点对比传统链式调用与Async/Await在代码可读性、参数传递及协程机制上的差异,阐述Async/Await如何简化异步逻辑并提升开发效率。
Promise 是什么
Promise 是处理异步编程的一种方案,旨在替代传统的回调函数和事件机制。ES6 规范统一了其用法,并在语言层面原生提供了 Promise 对象。作为一个对象,Promise 具备两个核心特性:
第一,对象的状态不受外界因素影响。
第二,状态一旦改变便不可逆转,这意味着在任何时刻,Promise 对象仅处于一种确定的状态。
Promise 对象包含三种状态:Pending(进行中)、Resolved(已完成/成功)以及 Rejected(已失败/拒绝)。初始状态为 Pending,若异步操作成功,则状态转为 Resolved 并触发 resolve 回调;若操作失败,则状态转为 Rejected 并触发 reject 回调。
可以通过调用 Promise 构造函数来实例化一个 Promise 对象。
var promise = new Promise((resolve,reject) => {
setTimeout(()=>{
console.log("hello world")
}, 2000)
})
Promise 构造函数接收一个执行函数作为参数,该函数包含两个由 JavaScript 引擎提供的参数:resolve 和 reject。其中,resolve 函数的作用是将 Promise 对象的状态从 Pending 转为 Resolved,并将操作结果作为参数传递出去;reject 函数的作用是将状态转为 Rejected,并将错误信息作为参数传递出去。示例代码如下:
function greet(){
var promise = new Promise(function(resolve,reject){
var greet = "hello world"
resolve(greet)
})
return promise
}
greet().then(v=>{
console.log(v)//*
})
Promise 的 then 方法用于注册状态改变时的回调。该方法接受三个可选参数:成功回调、失败回调以及前进回调(通常指 progress 回调,但在标准 Promise 中主要关注前两者)。一般情况下只需实现成功回调,其余为可选。Promise 的核心价值在于通过 then 方法传递状态,从而实现回调函数的链式调用。请先执行以下代码以理解其流程:
function greet () {
var promise = new Promise (function(resolve, reject){
var greet = "hello world"
resolve(greet)
})
return promise
}
var p = greet().then(v => {
console.log(v) // Promise { <pending> }
})
console.log(p) // hello world
catch 用法
function judgeNumber(num){
var promise1 = new Promise(function(resolve,reject){
num =5;
if(num<5){
resolve("num小于5,值为:"+num);
}else{
reject("num不小于5,值为:"+num);
}
});
return promise1;
}
judgeNumber().then(
function(message){
console.log(message);
}
)
.catch(function(message){
console.log(message);
})
Promise 的 all 方法用于并行执行多个异步操作,只有当 all 中包含的所有异步操作全部完成后,才会执行最终的回调函数。
function p1(){
var promise1 = new Promise(function(resolve,reject){
console.log("p1的第一条输出语句");
console.log("p1的第二条输出语句");
resolve("p1完成");
})
return promise1;
}
function p2(){
var promise2 = new Promise(function(resolve,reject){
console.log("p2的第一条输出语句");
setTimeout(()=>{console.log("p2的第二条输出语句");resolve("p2完成")},2000);
})
return promise2;
}
function p3(){
var promise3 = new Promise(function(resolve,reject){
console.log("p3的第一条输出语句");
console.log("p3的第二条输出语句");
resolve("p3完成")
});
return promise3;
}
Promise.all([p1(),p2(),p3()]).then(function(data){
console.log(data);
})
p1的第一条输出语句
p1的第二条输出语句
p2的第一条输出语句
p3的第一条输出语句
p3的第二条输出语句
p2的第二条输出语句
['p1完成', 'p2完成', 'p3完成']
在 all 方法的回调中,需等待所有 Promise 对象执行完毕后才触发回调;而 race 方法则不同,它只需等待第一个改变状态的 Promise 对象完成,随即执行回调。将上述代码中的 all 替换为 race,即可得到如下结果:
Promise.race([p1(),p2(),p3()]).then(function(data){
console.log(data);
})
如何实现一个 Promise
(function(window,undefined){
// resolve 和 reject 最终都会调用该函数
var final = function(status,value){
var promise = this, fn, st;
if(promise._status !== 'PENDING') return;
// 所以的执行都是异步调用,保证then是先执行的
setTimeout(function(){
promise._status = status;
st = promise._status === 'FULFILLED'
queue = promise[st ? '_resolves' : '_rejects'];
while(fn = queue.shift()) {
value = fn.call(promise, value) || value;
}
promise[st ? '_value' : '_reason'] = value;
promise['_resolves'] = promise['_rejects'] = undefined;
});
}
//参数是一个函数,内部提供两个函数作为该函数的参数,分别是resolve 和 reject
var Promise = function(resolver){
if (!(typeof resolver === 'function' ))
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
//如果不是promise实例,就new一个
if(!(this instanceof Promise)) return new Promise(resolver);
var promise = this;
promise._value;
promise._reason;
promise._status = 'PENDING';
//存储状态
promise._resolves = [];
promise._rejects = [];
//
var resolve = function(value) {
//由於apply參數是數組
final.apply(promise,['FULFILLED'].concat([value]));
}
var reject = function(reason){
final.apply(promise,['REJECTED'].concat([reason]));
}
resolver(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
var promise = this;
// 每次返回一个promise,保证是可thenable的
return new Promise(function(resolve,reject){
function handle(value) {
// 這一步很關鍵,只有這樣才可以將值傳遞給下一個resolve
var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value;
//判断是不是promise 对象
if (ret && typeof ret ['then'] == 'function') {
ret.then(function(value) {
resolve(value);
}, function(reason) {
reject(reason);
});
} else {
resolve(ret);
}
}
function errback(reason){
reason = typeof onRejected === 'function' && onRejected(reason) || reason;
reject(reason);
}
if(promise._status === 'PENDING'){
promise._resolves.push(handle);
promise._rejects.push(errback);
}else if(promise._status === FULFILLED){ // 状态改变后的then操作,立刻执行
callback(promise._value);
}else if(promise._status === REJECTED){
errback(promise._reason);
}
});
}
Promise.prototype.catch = function(onRejected){
return this.then(undefined, onRejected)
}
Promise.prototype.delay = function(ms,value){
return this.then(function(ori){
return Promise.delay(ms,value || ori);
})
}
Promise.delay = function(ms,value){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve(value);
console.log('1');
},ms);
})
}
Promise.resolve = function(arg){
return new Promise(function(resolve,reject){
resolve(arg)
})
}
Promise.reject = function(arg){
return Promise(function(resolve,reject){
reject(arg)
})
}
Promise.all = function(promises){
if (!Array.isArray(promises)) {
throw new TypeError('You must pass an array to all.');
}
return Promise(function(resolve,reject){
var i = 0,
result = [],
len = promises.length,
count = len
//这里与race中的函数相比,多了一层嵌套,要传入index
function resolver(index) {
return function(value) {
resolveAll(index, value);
};
}
function rejecter(reason){
reject(reason);
}
function resolveAll(index,value){
result[index] = value;
if( --count == 0){
resolve(result)
}
}
for (; i < len; i++) {
promises[i].then(resolver(i),rejecter);
}
});
}
Promise.race = function(promises){
if (!Array.isArray(promises)) {
throw new TypeError('You must pass an array to race.');
}
return Promise(function(resolve,reject){
var i = 0,
len = promises.length;
function resolver(value) {
resolve(value);
}
function rejecter(reason){
reject(reason);
}
for (; i < len; i++) {
promises[i].then(resolver,rejecter);
}
});
}
window.Promise = Promise;
})(window);
Async/Await
async 关键字用于声明一个异步函数,await 关键字仅能存在于 async 函数内部,不可单独使用。
async 函数返回的是一个 Promise 对象,而 await 的作用就是等待这个 Promise 对象返回结果,之后才继续执行后续代码。
await 等待的是一个 Promise 对象,其后必须跟随一个 Promise 实例。使用 await 时无需显式调用 then() 方法,即可直接获取返回值。
Async/Await 的优势
便于级联调用:适用于需要依次发生调用的场景;
同步代码编写风格:使用 Promise 的 then 函数进行链式调用时,代码呈现从左向右的横向结构(“点”操作);而 Async/Await 从上到下顺序执行,写法更接近同步代码,符合常规编程习惯;
多参数传递便捷:Promise 的 then 函数通常只传递一个参数,若需传递多个参数,往往需要将其包装为对象,这会导致信息冗余及频繁的参数解析与重组,较为繁琐;Async/Await 无此限制,可将结果视为普通局部变量,使用 let 或 const 定义的块级变量可自由使用,无数量限制且无冗余操作;
同步与异步代码混合编写:使用 Promise 时,建议将同步代码与异步代码分别置于不同的 then 节点中,以保持结构清晰;Async/Await 的书写习惯整体呈同步风格,无需纠结同步与异步的界限,只需将异步过程包装为 Promise 对象置于 await 关键字后即可;
基于协程机制:Promise 基于函数式编程范式,对异步过程进行封装;Async/Await 则基于协程机制,实现了真正的“保存上下文、切换控制权……恢复控制权、取回上下文”流程,是对异步过程更精确的描述;
Async/Await 是对 Promise 的优化:Async/Await 建立在 Promise 基础之上,是进一步的优化手段。在实际编码中,Promise 的底层 API 使用频率较低,代码风格更接近同步代码;
到此这篇关于Javascript promise异步编程浅析的文章就介绍到这了,更多相关Js promise异步内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!