这篇文章讲解 promise 各个方面的基本使用,从promise的状态、promise的创建、promise的值、promise的then、promise的链式调用、promise的catch等等内容,文章深入浅出,通俗易懂,目的是为了让初学者能够简单且全面地掌握promise
下面我们开始
promise 的状态
promise 的状态 status 有三种,一种的待定 pending,一种成功 fulfilled,一种是失败 rejected
promise 状态在某一时刻只能为一种
promise 的状态如果从待定状态变成了 fulfilled,或者是 rejected 后,就不能变成其他的状态了
promise 对象的创建
创建 promise 对象,通过 Promise 构造函数
这个构造函数接受一个函数作为参数。在创建 promise 对象的过程中,会调用传入的参数,并且传入两个函数 resolve,和 reject
const promise = new Promise((resolve,reject)=>{});
上面创建了一个 promise 示例,这个 promise 的状态是 pending
如果我们想改变这个 promise 对象的状态,就需要调用其中的 resolve 函数,或者是 reject 函数
const promise = new Promise((resolve, reject)=>{
resolve();
})
状态变成了fulfilled
const promise = new Promise((resolve, reject)=>{
reject();
})
状态变成了 rejected
可以很清楚的看到,当我们调用 resolve 或者 reject 之后,promise 的状态发生了变化。调用 resolve 参数会将 promise 的状态变成 fulfilled, 或者调用 reject 参数会将 promise 的状态的变成 rejected
我们还可以尝试,在调用了 resolve()之后,再调用 reject()会发生什么?
没有报错,状态也没有发生变化,还是保持fulfilled, 这是符合预期的。因为 promise 的状态一旦落定,就不能更改。
promise 的值
在调用 resolve()的时候,我们还可以向其传入实参:
const promise = new Promise((resolve, reject)=>{
resolve(1);
})
可以从图中看到,打印出来的 promise 不仅仅有状态的信息,还有 result,这个 result 的值是 1,正好是我们传入的实参。
这个 result 就是指 promise 的值
resolve 时候传入的值叫成功的值,reject 的值就是失败的值了
const promise = new Promise((resolve, reject)=>{
reject(1);
})
我们虽然可以看见 promise 的值,但是应该如何获取其中的值呢?
promise 对象,包括其原型对象中并没有类似 value,或者 getValue 属性,让我们获取其中的值,所以这条路是行不通的,得通过 then 函数:
const promise = new Promise((resolve, reject)=>{
resolve(1);
}).then(
res=>{
console.log('fulfilled: ', res);
},
err=>{
console.log('rejected: ', err)
}
)
then 函数接受两个函数作为参数,当 promise 的状态是fulfilled, 就会调用第一个参数,当其状态为 rejected,就会调用第二个参数。
then 的反复调用
我们还可以反复的在同一个 promise 上面调用 then 函数:
const promise = new Promise((resolve, reject)=>{
resolve(1);
})
promise.then(
res=>{
console.log('fulfilled1: ', res);
},
err=>{
console.log('rejected1: ', err)
}
)
promise.then(
res=>{
console.log('fulfilled2: ', res);
},
err=>{
console.log('rejected2: ', err)
}
)
promise.then(
res=>{
console.log('fulfilled3: ', res);
},
err=>{
console.log('rejected3: ', err)
}
)
打印结果:
可以看到,promise 的值都顺利拿到了。
反复调用的 then 的含义就意味着反复读取 promise 中的值!
promise 的异步性
下面看 promise 的异步性
console.log('start');
setTimeout(()=>{
console.log(1);
},0);
console.log('end');
上面代码打印的结果是什么?
可以看到最后打印了 1,因为 setTimeout 中代码是异步执行。即使其是 0ms 之后执行的!
再看下面的代码:
console.log('start');
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
resolve(1);
}).then(
res=>{
console.log('fulfilled: ', res);
},
err=>{
console.log('rejected: ', err)
}
)
console.log('end');
上面的代码执行结果是什么呢?
promise resolve在 start 和 end 中间打印出来,Promise 构造函数的代码是同步代码。而fulfilled:1在 end 后面调用,所以 then 里面的代码是异步代码!
promise 的微任务性
上面是简单的,显而易见的结论,下面深入一下
then 的参数函数是异步代码,在实际执行过程中,会将函数包裹成一个 task,放到微任务队列中,与 setTimeout 不相同,setTimeout 中的函数虽说也是异步,但其会将函数包裹成宏任务,放到定时队列中。
关于更多的微任务和宏任务,定时任务等新名词,这里简单解释一下,完全了解还得看这篇文章:JS-浏览器的任务队列
浏览器有两种任务,宏任务和微任务。其中宏任务又分为普通任务,和定时任务。
一般 script 脚本,或者用户交互(点击,点击键盘)执行的脚本,都是普通的宏任务。产生的宏任务会被放在宏任务队列中,等待主线程执行。
setTimout,setInterval生成的任务,是定时任务。定时任务刚开始会被放在定时任务队列中,等定时任务时间一到,就会被放到普通宏任务队列中,等到主线程执行。
微任务,刚产生也是被放在一个队列中的,这个队列叫微任务队列。
执行顺序:浏览器会先执行宏任务,然后再执行所有的微任务;然后是下一个宏任务,然后再是所有的微任务,如此循环。
在浏览器每执行完当前的宏任务和所有的微任务,就会去定时任务队列中扫描,看看有没有计时结束的任务,如果有,就将其取出,并放入宏任务队列中。
要是处理微任务队列的过程中,产生的了新的微任务,依旧直接放入微队列中。直到所有的微任务都被执行,主线程就会执行下一个宏任务
then 函数中的异步任务是微任务,所以优先级高于 setTimeout:
console.log('start');
setTimeout(()=>{
console.log(1);
},0);
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
resolve(1);
}).then(
res=>{
console.log('fulfilled: ', res);
},
err=>{
console.log('rejected: ', err)
}
)
console.log('end');
then 的返回值
then 函数也是有返回值的,它的返回值是一个新的 promise!
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
resolve(1);
})
const promise1 = promise.then(
res=>{
console.log('fulfilled: ', res);
},
err=>{
console.log('rejected: ', err)
}
)
console.log(promise !== promise1); // true
拿到一个 promise,我们关心什么?关心它的状态,以及它的值。那生成的新的 promise 的状态和值是什么呢?
其实,新生成的 promise 的状态和值由 then 里的参数函数决定。
then 第一个参数是处理 promise 成功的状态,一般称其为 onFulfilled;第二个参数是处理 promise 失败的状态,一般称其为 onRejected。
下面就用 onFulfilled 的返回值举个例子:
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
resolve(1);
})
const promise1 = promise.then(
res=>{
console.log('fulfilled: ', res);
return 200;
},
err=>{
console.log('rejected: ', err)
}
)
console.log(promise1);
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
resolve(1);
})
const promise1 = promise.then(
res=>{
console.log('fulfilled: ', res);
return {name: 'zenos'};
},
err=>{
console.log('rejected: ', err)
}
)
console.log(promise1);
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
resolve(1);
})
const promise1 = promise.then(
res=>{
console.log('fulfilled: ', res);
return new Promise((resolve, reject)=>{
resolve('love-blue');
});
},
err=>{
console.log('rejected: ', err)
}
)
console.log(promise1);
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
resolve(1);
})
const promise1 = promise.then(
res=>{
console.log('fulfilled: ', res);
return new Promise((resolve, reject)=>{
reject('zenos');
});;
},
err=>{
console.log('rejected: ', err)
}
)
console.log(promise1);
看完上面四个很简单的例子,相信你肯定已经知道 then 返回的新 promise 和 onFulfilled 之间是什么关系
onFulfilled可以返回任意值,如果返回的是非 promise 值,那么生成的新 promise 的状态都是fulfilled,并且它的值和 onFulfilled 的返回值一致- 如果
onFulfilled返回的是promise对象,那么生成的新promise的状态和返回的promise的状态一致,且值也是一致的。
好,这个点搞懂了,那么 then 返回一个新的 promise 意味着什么呢?
意味着可以链式调用 then!
promise 的链式调用
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
resolve(1);
})
const promise1 = promise
.then(
res=>{
console.log('fulfilled: ', res);
return 'zenos'
},
err=>{
console.log('rejected: ', err)
}
)
.then(
res=>{
console.log('fulfilled2: ', res);
return 'blue'
},
err=>{
console.log('rejected: ', err)
}
)
打印一下:
打印结果第一行是 promise resolve,是因为同步执行了构造函数的参数,所以打印出来了
打印结果第二行是fulfilled:1,是因为上一个 promise 对象的状态是成功的,并且值为 1
打印结果第三行是fulfilled2: zenos,是因为第一个 then 返回了一个普通值zenos,所以其返回的新 promise 的状态为 fulfilled,并且值为 zenos
其实上面的代码还可以简化成:
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
resolve(1);
}).then(
res=>{
console.log('fulfilled: ', res);
return 'zenos'
},
err=>{
console.log('rejected: ', err)
}
)
.then(
res=>{
console.log('fulfilled2: ', res);
return 'blue'
},
err=>{
console.log('rejected: ', err)
}
)
⚠️注意:现在代码中 promise 对象,就是第二个 then 返回的对象咯
promise 的失败不传染性
如果刚开始 promise 的状态是 rejected,会发生什么呢?
const promise = new Promise((resolve, reject)=>{
console.log('promise resolve');
reject(2);
}).then(
res=>{
console.log('fulfilled: ', res);
return 'zenos'
},
err=>{
console.log('rejected: ', err);
}
)
.then(
res=>{
console.log('fulfilled2: ', res);
return 'blue'
},
err=>{
console.log('rejected: ', err);
}
)
打印结果:
第一行打印结果意料之中。第二行打印结果是rejected:2,是因为上一个 promise 的状态是 rejected,所以 then 中的第二个回调函数被执行;并且失败的值是 2, 所以 err 的值为 2
第三行的打印结果为 fulfilled2: undefined, 这是执行了第二个 then 中的onFulfilled(then 的第一个回调),并且 res 的值为 undefined。
可以推断出,第一个 then 返回的 promise 的状态是 fulfilled,并且值为 undefined!
其实 then 返回的 promise 对象的状态和值,不仅仅由onFulfilled(then 的第一个回调)决定,也由onRejected(then 的第二个回调)决定。并且决定的规则是相同的。
onRejected可以返回任意值,如果返回的是非promise值,那么生成的新promise的状态都是fulfilled,并且它的值和onFulfilled的返回值一致- 如果
onRejected返回的是promise的值,那么生成的新promise的状态和返回的promise的状态一致,且值也是一致的。
这就是 then 中的失败不传染性。
失败的 promise 在前面 then 中被解决了,那么后面的 then 得到的就是 fulfilled 的 promise
如果我想将失败的状态往后传,应该怎么做呢? 很简单啊,我在onRejected中返回一个失败状态的 promise 不就可以了?
const promise = new Promise((resolve, reject) => {
console.log("promise resolve");
reject(2);
})
.then(
(res) => {
console.log("fulfilled: ", res);
return "zenos";
},
(err) => {
console.log("rejected: ", err);
return new Promise((resolve, reject) => {
reject(err);
});
}
)
.then(
(res) => {
console.log("fulfilled2: ", res);
return "blue";
},
(err) => {
console.log("rejected: ", err);
}
);
打印结果:
还有一种不推荐的做法,在onRejected中抛出错误也可以:
const promise = new Promise((resolve, reject) => {
console.log("promise resolve");
reject(2);
})
.then(
(res) => {
console.log("fulfilled: ", res);
return "zenos";
},
(err) => {
console.log("rejected: ", err);
throw err;
}
)
.then(
(res) => {
console.log("fulfilled2: ", res);
return "blue";
},
(err) => {
console.log("rejected: ", err);
}
);
其实,无论在onRejected还是 onResolved抛出错误,都不会导致整个的程序崩溃,只会让当前的 then 返回的 promise 变成失败的状态,并且 promise 的值为错误抛出的内容。
promise 中的 catch
很多时候,我们在使用 then 的链式调用时,处理错误的方式是一致的,并且只想处理一遍,不想每个then 中都处理一次,then 支持只传入onFulfilled,省略onRejected,失败的 promise 遇到这种 then,会跳过这个 then,并将失败的 promise 向后传递:
const promise = new Promise((resolve, reject) => {
console.log("promise resolve");
reject(2);
})
.then((res) => {
console.log("fulfilled: ", res);
return "zenos";
})
.then(
(res) => {
console.log("fulfilled2: ", res);
return "blue";
},
(err) => {
console.log("rejected: ", err);
}
);
打印结果有两行,第二个 then 顺利处理了失败的 promise。
看着是不是像第一个 then 被跳过了
promise 还支持这种写法:
const promise = new Promise((resolve, reject) => {
console.log("promise resolve");
reject(2);
})
.then((res) => {
console.log("fulfilled: ", res);
return "zenos";
})
.then((res) => {
console.log("fulfilled2: ", res);
return "blue";
})
.catch((err) => {
console.log("catch: ", err);
});
then 中一律省略onRejected参数,在所有的 then 后面接一个.catch,用来处理链式调用过程中出现所有错误。
catch 有点像 try...catch的写法,catch 中的代码,在 try 中抛出错误之后,就会被执行。而 promise中的 catch回调函数 ,在链式调用过程中抛出了错误,就会被执行。
promise 中的 finally
除了 catch,promise 还支持 finally,不过这里的finally和try...catch..finally的finally不一样,try...catch中 finally表示 try 中是否出现错误都会执行,并且,无论 try 或 catch 中是否有 return,都会执行。
但是 promise 中 finally 并不是那个意思。finally 中回调函数,无论 promise 是失败还是成功,都会执行,不过 finally 中拿不到 promise 的值。 finally 后面还可以继续 then(因为 finally 也会返回 promise), 并且会原封不动的将状态和值传递给后面的 then
const promise = new Promise((resolve, reject) => {
console.log("promise resolve");
reject(2);
})
.then((res) => {
console.log("fulfilled: ", res);
return "zenos";
})
.then((res) => {
console.log("fulfilled2: ", res);
return "blue";
})
.finally((res) => {
console.log("end1: ", res);
})
.catch((err) => {
console.log("catch: ", err);
return "catch err";
})
.finally((res) => {
console.log("end2: ", res);
})
.then((res) => {
console.log("fulfilled3: ", res);
});
尽管 finally 有很多需要注意的性质,但在开发中,我们还是习惯将其放在链式 then 的最后,用来表示最后执行的动作。
promise 的中文含义
promise 的中文含义是承诺,表示它承诺,当它的状态落定后,一定做某某事情。
这个状态落定的意思是 promise 的状态从 pending 变成 fulfilled,或者变成 rejected。
一定做某某事情的意思是,通过 then 来“注册”的回调函数,一定会被执行。不过成功后执行成功的回调,失败后执行失败的回调。
至于什么时候状态落定,就看”天意“了哈哈哈哈
看个例子:
fetch("/all-tasks")
.then((res) => res.json())
.then((res) => {
const data = res.data;
return data;
})
.catch(err=>{
console.log('请求接口失败: ',err);
});
这是一个经典的请求接口的代码。通过 fetch 访问/all-tasks接口,fetch()会返回一个 promise。
后面的then就表示处理接口返回的数据了。但是这个 then 中回调函数什么时候会被执行呢??
答案是天晓得,什么时候接口请求成功了,什么时候fetch返回的 promise 状态落定了,then 中回调函数自然就会被执行了。
用承诺的角度看,promise 承诺,在接口请求成功的时候,执行:
(res) => res.json()
以及:
(res) => {
const data = res.data;
return data;
}
如果接口请求失败了,就会执行:
err=>{
console.log('请求接口失败: ',err);
}
总结
这篇文章讲了 promise 的基本使用,从 promise 的创建,promise 的异步性,promise 的微任务性,promise 的then 重复调用,promise 的链式 then 调用,promise 的失败不传染性,还介绍了一些其他的 API,有 catch,finally
希望这篇文章的介绍可以帮助你初步掌握 promise 的使用,下篇文章,我们来深入学习 promise 的实战应用