Promise: ES6 新增的語法,是異步問題同步化解決方案 ,同時解決了回調(diào)地獄的問題;
Promise解決:
通過.then的方式,拿到Promise內(nèi)部的異步操作的結(jié)果,并且不阻塞 和Promise不相關(guān)的 任何程序,讓它們同步執(zhí)行;
then 函數(shù)會返回一個 Promise 實例,并且該返回值是一個新的實例而不是之前的實例。因為 Promise 規(guī)范規(guī)定除了 pending 狀態(tài),其他狀態(tài)是不可以改變的,如果返回的是一個相同實例的話,多個 then 調(diào)用就失去意義了。 對于 then 來說,本質(zhì)上可以把它看成是 flatMap;Promise的狀態(tài)(生命周期):
padding:進行中
fulfilled:已完成
rejected:已失敗異步任務(wù)的完成與否 取決于當前 Promise的狀態(tài)
1.異步任務(wù)完成 調(diào)用resolve()成功回調(diào) 狀態(tài):padding -> fulfilled
2.異步任務(wù)失敗 調(diào)用reject()失敗回調(diào) 狀態(tài):padding -> rejectedPromise的特點:
1.Promise的狀態(tài)不受外界的影響;
因為Promise代表的是異步操作,只有異步操作成功或者或者失敗,才會改變* Promise的狀態(tài);
2.Promise的狀態(tài)一旦改變就無法更改;
一旦狀態(tài)固化,返回的結(jié)果在任何地方都調(diào)用的到;Promise的執(zhí)行方式:
微任務(wù)的方式執(zhí)行Promise注冊上回調(diào)函數(shù),不管是成功還是失敗都可以;
微任務(wù):相當于高速中的緊急車道,可以讓微任務(wù)快速排在異步隊列的最前方,等待主線程通過事件輪詢將其調(diào)用;Promise的靜態(tài)方法:(靜態(tài)方法是在構(gòu)造器中綁定的方法)
Promise.resolve():返回一個成功狀態(tài)態(tài)的Promise,通過.then()注冊成功的回調(diào)函數(shù);
Promse.reject():返回一個失敗狀態(tài)的Promise,通過.catch()注冊失敗的回調(diào)函數(shù);
Promise.all([p1,p2,p3]):只有當中的Promise對象全部成功,才會返回一個結(jié)果且是個數(shù)組;
Promise.race([p1,p2,p3]):返回其中一個運行最快的結(jié)果;
let p = new Promise((resolve,reject) => { //Promise中的回調(diào)函數(shù)是:執(zhí)行器函數(shù)(executon)===同步執(zhí)行
console.log('我是Promise的同步回調(diào)函數(shù)');
//異步任務(wù)
$.ajax({
url:'./data.json',
success:function (data) {
// console.log(data)
resolve(data)
},
error:function (err) {
reject(err)
}
})
p.then((res) => { //.then方式注冊的是 成功的回調(diào)函數(shù)
// console.log(res)
// simpleArray(res)
console.log( simpleArray(res))
}).catch((err) => { //.catch方式注冊的是 失敗的回調(diào)函數(shù)
console.log(err,'失敗')
})
console.log('我是同步代碼')
function simpleArray (data) {
return data.map(function (item) {
// console.log(item)
return item.name
})
}
// 我是Promise的同步回調(diào)函數(shù)
// 我是同步代碼
// ["zhangsan", "lisi", "wangwu"]
- 面試考點:theable 對象
1.Promise調(diào)用resolve方法傳入theable對象會發(fā)生什么?
會調(diào)用theable對象中的 then方法,然后調(diào)用resolve()傳出成功結(jié)果,或者調(diào)用reject()傳出失敗結(jié)果;
2.Promise調(diào)用reject方法傳入theable對象會發(fā)生什么?
Promise只有調(diào)用resolve方法傳入theable對象才會返回成功或失敗的結(jié)果,
調(diào)用reject()只會返回一個空對象{then:f}
//theable 對象
let obj = {
then(resolve,reject){
// resolve(11);
reject(12);
}
}
let p1 = Promise.resolve(obj);
// let p1 = Promise.reject(obj);//{then:f}
p1.then((data) => {
console.log(data)
}).catch((err) => {
console.log(err)
})
- 案例
//考察:異步隊列 事件輪詢 微任務(wù)
Promise.resolve().then(function(){
console.log('Promise1') //1
setTimeout(() => {
console.log('setTimeOut1') //4
},0)
})
setTimeout(() => {
console.log('setTimeOut2') //2
Promise.resolve().then(function(){
console.log('Promise2') //3
})
},0)
//Promise1
//setTimeOut2
//Promise2
//setTimeOut1
首先看出來,Promise是通過構(gòu)造函數(shù)實例化一個對象,然后通過實例對象上的then方法,來處理異步返回的結(jié)果。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function Promise(executor) {
var _this = this
this.state = PENDING; //狀態(tài)
this.value = undefined; //成功結(jié)果
this.reason = undefined; //失敗原因
function resolve(value) {}
function reject(reason) {}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
};
module.exports = Promise;
當我們實例化Promise時,構(gòu)造函數(shù)會馬上調(diào)用傳入的執(zhí)行函數(shù)executor,我們可以試一下:
let p = new Promise((resolve, reject) => {
console.log('執(zhí)行了');
});
因此在Promise中構(gòu)造函數(shù)立馬執(zhí)行,同時將resolve函數(shù)和reject函數(shù)作為參數(shù)傳入:
function Promise(executor) {
var _this = this
this.state = PENDING; //狀態(tài)
this.value = undefined; //成功結(jié)果
this.reason = undefined; //失敗原因
function resolve(value) {}
function reject(reason) {}
executor(resolve, reject)
}
但是executor也會可能存在異常,因此通過try/catch來捕獲一下異常情況:
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
- 手寫Promise
function Mypromise(fn) { //構(gòu)造函數(shù)
this.state = 'pending' //狀態(tài)
this.value = undefined //成功結(jié)果
this.reason = undefined //失敗原因
/*
那么如何讓我們的Promise來支持異步呢?
我們可以參考發(fā)布訂閱模式,在執(zhí)行then方法的時候,如果當前還是PENDING狀態(tài),
就把回調(diào)函數(shù)寄存到一個數(shù)組中,當狀態(tài)發(fā)生改變時,去數(shù)組中取出回調(diào)函數(shù);
*/
this.onFulfilled = [];//成功的回調(diào)
this.onRejected = []; //失敗的回調(diào)
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onFulfilled.forEach(fn => fn(value))
// console.log(this.value)
}
}
let reject = value => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = value
this.onRejected.forEach(fn => fn(value))
console.log(this.reason)
}
}
// 自動執(zhí)行函數(shù)
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
// then
}
/*
在規(guī)范中說明了,onFulfilled和onRejected是可選的,因此我們對兩個值進行一下類型的判斷:
onFulfilled 和 onRejected 都是可選參數(shù)。
如果 onFulfilled 不是函數(shù),其必須被忽略。
如果 onRejected 不是函數(shù),其必須被忽略。
*/
Mypromise.prototype.then = function (onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
typeof onFulfilled === 'function' && onFulfilled(this.value)
}
if (this.state === 'rejected') {
typeof onRejected === 'function' && onRejected(this.reason)
}
if (this.state === 'pending') {
typeof onFulfilled === 'function' && this.onFulfilled.push(onFulfilled)
typeof onRejected === 'function' && this.onRejected.push(onRejected)
}
}
//Promise:異步問題同步化解決方案
let p = new Mypromise((resolve, reject) => { //Promise中的回調(diào)函數(shù)是:執(zhí)行器函數(shù)(executon)===同步執(zhí)行
console.log('我是Promise的同步回調(diào)函數(shù)');
//異步任務(wù)
$.ajax({
url: './data.json',
success: function (data) {
console.log(data)
resolve(data)
},
error: function (err) {
reject(err)
}
})
})
p.then((res) => { //.then方式注冊的是 成功的回調(diào)函數(shù)
console.log(res)
// simpleArray(res)
console.log(simpleArray(res))
})
function simpleArray(data) {
return data.map(function (item) {
// console.log(item)
return item.name
})
}
// 我是Promise的同步回調(diào)函數(shù)
// 我是同步代碼
// ["zhangsan", "lisi", "wangwu"]
手寫Promise參考原文:
從零開始手寫Promise
實踐系列-Promises/A+規(guī)范
- 手寫Promise(用Class類)
class myPromise {
constructor(fn) {
this.state = 'pending' //狀態(tài)
this.value = undefined //成功結(jié)果
this.reason = undefined //失敗原因
this.onFulfilled = [];//成功的回調(diào)
this.onRejected = []; //失敗的回調(diào)
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onFulfilled.forEach(fn => fn(value))
// console.log(this.value)
}
}
let reject = value => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = value
this.onRejected.forEach(fn => fn(value))
console.log(this.reason)
}
}
// 自動執(zhí)行函數(shù)
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
// then
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
typeof onFulfilled === 'function' && onFulfilled(this.value)
}
if (this.state === 'rejected') {
typeof onRejected === 'function' && onRejected(this.reason)
}
if (this.state === 'pending') {
typeof onFulfilled === 'function' && this.onFulfilled.push(onFulfilled)
typeof onRejected === 'function' && this.onRejected.push(onRejected)
}
}
}
//Promise:異步問題同步化解決方案
let p = new myPromise((resolve, reject) => { //Promise中的回調(diào)函數(shù)是:執(zhí)行器函數(shù)(executon)===同步執(zhí)行
console.log('我是Promise的同步回調(diào)函數(shù)');
//異步任務(wù)
$.ajax({
url: './data.json',
success: function (data) {
console.log(data)
resolve(data)
},
error: function (err) {
reject(err)
}
})
})
p.then((res) => { //.then方式注冊的是 成功的回調(diào)函數(shù)
console.log(res)
// simpleArray(res)
console.log(simpleArray(res))
})
function simpleArray(data) {
return data.map(function (item) {
// console.log(item)
return item.name
})
}
// 我是Promise的同步回調(diào)函數(shù)
// 我是同步代碼
// ["zhangsan", "lisi", "wangwu"]
- async & await:
原理:async 和 await 用了同步的方式去做異步,async 定義的函數(shù)的返回值都是 promise,await后面的函數(shù)會先執(zhí)行一遍,然后就會跳出整個 async 函數(shù)來執(zhí)行后面js棧的代碼;
主要考察宏任務(wù)和微任務(wù),搭配promise,詢問一些輸出的順序;