溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Javascript如何創(chuàng)建Promise?

發(fā)布時間:2020-05-20 10:46:34 來源:億速云 閱讀:398 作者:Leah 欄目:web開發(fā)

這篇文章主要為大家詳細(xì)介紹了Javascript創(chuàng)建Promise的方法,文中示例代碼介紹的非常詳細(xì),零基礎(chǔ)也能參考此文章,感興趣的小伙伴們可以參考一下。

executor函數(shù)

我們知道,在創(chuàng)建一個Promise實(shí)例時,都會立即執(zhí)行executor函數(shù),executor函數(shù)傳遞兩個參數(shù),resolve和reject,如果executor函數(shù)執(zhí)行錯誤,Promise實(shí)例狀態(tài)會變?yōu)閞ejected

class MyPromise{
    constructor(executor) {
        this.status = "pending";     // 初始化狀態(tài)為pending
        this.value = undefined;      // 初始化返回的成功的結(jié)果或者失敗的原因
        
        // 這里是resolve方法,成功后執(zhí)行,將狀態(tài)改變?yōu)閞esolved,并且將結(jié)果返回
        let resolve = result => {
            if(this.status !== "pending") return;  // 狀態(tài)一旦改變,就不會再變
            this.status = "resolved";
            this.value = result;
        }
        
        // 這里是reject方法,異常時執(zhí)行,狀態(tài)改為rejected,并且將失敗的原因返回
        let reject = reason => {
            if(this.status !== "pending") return;
            this.status = "rejected";
            this.value = reason;
        }
        // try、catch捕獲異常,如果錯誤,執(zhí)行reject方法
        try {
            executor(resolve, reject)
        } catch(err) {
            reject(err)
        }
    }
}

我們來驗證一下,現(xiàn)在的Promise是什么樣的

let p1 = new MyPromise((resolve, reject) => {
    resolve(1);
})
let p2 = new MyPromise((resolve, reject) => {
    reject(2);
})
console.log(p1);
console.log(p2);

可以看到,狀態(tài)已經(jīng)改變了,里面的值也是成功的結(jié)果和失敗的原因。then方法有兩個參數(shù),第一個參數(shù)是成功時執(zhí)行的,第二個參數(shù)為失敗后執(zhí)行的,then的鏈?zhǔn)秸{(diào)用和數(shù)組等是一樣的,每次執(zhí)行后會返回一個Promise實(shí)例。如果成功后,第一個then中成功的函數(shù)為null,它會繼續(xù)向下查找,直至不為null的函數(shù)執(zhí)行,上一個then中返回的結(jié)果會直接影響下一個then中執(zhí)行成功或者失敗的哪個函數(shù),了解了這些之后,我們嘗試實(shí)現(xiàn)一下~

then方法

then(resolveFn, rejectFn) {
    // 如果傳入的兩個參數(shù)不是函數(shù),則直接執(zhí)行返回結(jié)果
    let resolveArr = [];
    let rejectArr = [];
    
    if(typeof resolveFn !== "function") {
        resolveFn = result => {
            return result;
        }
    }
    
    if(typeof rejectFn !== "function") {
        rejectFn = reason => {
            return MyPromise.reject(reason);
        }
    }
    
    return new Mypromise((resolve, reject) => {
        resolveArr.push(result => {
            try {
                let x = resolveFn(result);
                
                if(x instanceof MyPromise) {
                    x.then(resolve, reject)
                    return;
                }
                
                resolve(x);
            } catch(err) {
                reject(err)
            }
        })
        
        rejectArr.push(reason => {
            try {
                let x = rejectFn(reason);
                
                if(x instanceof MyPromise) {
                    x.then(resolve, reject)
                    return;
                }
                
                resolve(x);
            } catch(err) {
                reject(err)
            }
        })
    })
}

我們來整理一下上面的代碼

class MyPromise{
    constructor(executor) {
        this.status = "pending";     // 初始化狀態(tài)為pending
        this.value = undefined;      // 初始化返回的成功的結(jié)果或者失敗的原因
        this.resolveArr = [];        // 初始化then中成功的方法
        this.rejectArr = [];         // 初始化then中失敗的方法
        
        
        // 定義change方法,因為我們發(fā)現(xiàn)好像resolve和reject方法共同的地方還挺多
        let change = (status, value) => {
            if(this.status !== "pending") return;  // 狀態(tài)一旦改變,就不會再變
            this.status = status;
            this.value = value;
            
            // 根據(jù)狀態(tài)判斷要執(zhí)行成功的方法或失敗的方法
            let fnArr = status === "resolved" ? this.resolveArr : this.rejectArr;
            
            // fnArr中的方法依次執(zhí)行
            fnArr.forEach(item => {
                if(typeof item !== "function") return;
                item(this. value);
            })
        }
        // 這里是resolve方法,成功后執(zhí)行,將狀態(tài)改變?yōu)閞esolved,并且將結(jié)果返回
        let resolve = result => {
            change("resolved", result)
        }
        
        // 這里是reject方法,異常時執(zhí)行,狀態(tài)改為rejected,并且將失敗的原因返回
        let reject = reason => {
            change("rejected", reason);
        }
        
        // try、catch捕獲異常,如果錯誤,執(zhí)行reject方法
        try {
            executor(resolve, reject)
        } catch(err) {
            reject(err)
        }
    }
    
    then(resolveFn, rejectFn) {
    // 如果傳入的兩個參數(shù)不是函數(shù),則直接執(zhí)行返回結(jié)果
    
        if(typeof resolveFn !== "function") {
            resolveFn = result => {
                return result;
            }
        }
        
        if(typeof rejectFn !== "function") {
            rejectFn = reason => {
                return MyPromise.reject(reason);
            }
        }
        
        return new MyPromise((resolve, reject) => {
            this.resolveArr.push(result => {
                try {
                    let x = resolveFn(result);  // 獲取執(zhí)行成功方法返回的結(jié)果
                    
                    // 如果x是一個promise實(shí)例,則繼續(xù)調(diào)用then方法 ==> then鏈的實(shí)現(xiàn)
                    if(x instanceof MyPromise) {
                        x.then(resolve, reject)
                        return;
                    }
                    
                    // 不是promise實(shí)例,直接執(zhí)行成功的方法
                    resolve(x);
                } catch(err) {
                    reject(err)
                }
            })
            
            this.rejectArr.push(reason => {
                try {
                    let x = rejectFn(reason);
                    
                    if(x instanceof MyPromise) {
                        x.then(resolve, reject)
                        return;
                    }
                    
                    resolve(x);
                } catch(err) {
                    reject(err)
                }
            })
        })
    }
}

我們來看一下效果

new MyPromise((resolve, reject) => {
    resolve(1);
}).then(res => {
    console.log(res, 'success');
}, err => {
    console.log(err, 'error');
})

這時候,問題出現(xiàn)了,我們發(fā)現(xiàn)好像什么也沒有輸出,如果我們對上面的測試?yán)幼鲆幌滦⌒〉母膭幽兀?/p>

new MyPromise((resolve, reject) => {
    setTimeout(_ => {
        resolve(1);
    }, 0)
}).then(res => {
    console.log(res, 'success');    // 1 "success"
}, err => {
    console.log(err, 'error');
})

這是因為創(chuàng)建了Promise實(shí)例就立即執(zhí)行了executor函數(shù),還沒有執(zhí)行then方法,那么不管成功還是失敗的數(shù)組中,都是空的。那可能小伙伴們又有疑問了,為什么加了setTimeout就好使了呢?這是因為在事件隊列機(jī)制中,setTimeout會放入事件隊列中,等主線程執(zhí)行完成后再執(zhí)行,此時then方法會存儲成功或者失敗的函數(shù),所以不管是成功的數(shù)組還是失敗的數(shù)組中都已經(jīng)有值了,這個時候再去執(zhí)行就可以了。

但是我們不能在使用的時候?qū)憇etTimeout當(dāng)做解決方案呀,既然我們在封裝,就要在封裝的函數(shù)內(nèi)解決問題,按照這樣的思路,我們也同樣可以在resolve和reject方法執(zhí)行的時候,判斷數(shù)組中是否有值,如果沒有,我們可以利用setTimeout讓它延后執(zhí)行,代碼如下:

// 這里是resolve方法,成功后執(zhí)行,將狀態(tài)改變?yōu)閞esolved,并且將結(jié)果返回
let resolve = result => {   
    // 如果數(shù)組中有值,則立即改變狀態(tài)
    if(this.resolveArr.length > 0) {
        change("resolved", result)
    }
    // 如果沒值,則延后改變狀態(tài)
    let timer = setTimeout(_ => {
        change("resolved", result)
        clearTimeout(timer);
    }, 0)
}
// 這里是reject方法,異常時執(zhí)行,狀態(tài)改為rejected,并且將失敗的原因返回
let reject = reason => {
// 如果數(shù)組中有值,則立即改變狀態(tài)
    if(this.rejectArr.length > 0) {
        change("rejected", reason);
    }
    // 如果沒值,則延后改變狀態(tài)
    let timer = setTimeout(_ => {
        change("rejected", reason);
        clearTimeout(timer);
    }, 0)
}

現(xiàn)在我們再試一下

// 1、已經(jīng)成功了
new MyPromise((resolve, reject) => {
    resolve('我成功啦,吼吼吼~~~~');            
    reject('我都已經(jīng)成功了,你別想讓我失敗,哼~~');
}).then(res => {
    console.log(res, 'success');         // 我成功啦,吼吼吼~~~~ success
}, err => {
    console.log(err, 'error');
})
// 2、先失敗了
new MyPromise((resolve, reject) => {
    reject('失敗了,我好委屈,嗚嗚嗚~~');
    resolve('已經(jīng)失敗了~~~');            
}).then(res => {
    console.log(res, 'success');         
}, err => {
    console.log(err, 'error');          // 失敗了,我好委屈,嗚嗚嗚~~ error
})
// 3、鏈?zhǔn)秸{(diào)用
new MyPromise((resolve, reject) => {
    reject('失敗了,我好委屈,嗚嗚嗚~~');
    resolve('已經(jīng)失敗了~~~');            
}).then(res => {
    console.log(res);
}, err => {
    console.log(err, 'error');          // 失敗了,我好委屈,嗚嗚嗚~~ error
    return '我要發(fā)奮圖強(qiáng),不會被困難所擊倒,我要成功!?。?#39;
}).then(res1 => {
    console.log(res1, '經(jīng)過不懈努力,我終于在第二次成功了~');  // 我要發(fā)奮圖強(qiáng),不會被困難所擊倒,我要成功?。?!  經(jīng)過不懈努力,我終于在第二次成功了~
}, err1 => {
    console.log(err1, '第二次失敗');
})

這就完美解決了第一次調(diào)用,不會執(zhí)行then方法的問題。同時,實(shí)現(xiàn)了鏈?zhǔn)降恼{(diào)用。對于鏈?zhǔn)降恼{(diào)用,其實(shí)不管是數(shù)組的鏈?zhǔn)秸{(diào)用,都是因為上一次返回的還是此實(shí)例。

catch方法

catch方法是捕獲異常,它和then方法的第二個回調(diào)函數(shù)是一樣的

catch(rejectFn) {
    return this.then(null, rejectFn)
}

resolve方法

我們知道,Promsie也可以這樣用

let p1 = MyPromise.resolve(1);
console.log(p1);

我們期望有這樣一種寫法,但是現(xiàn)在肯定會拋出錯誤:MyPromise.resolve不是一個方法

現(xiàn)在需要我們封裝一下resolve方法,我們需要明確的是,resolve之后,Promise是支持再繼續(xù)鏈?zhǔn)秸{(diào)用then的,所以,我們需要執(zhí)行resolve方法,返回一個Promise實(shí)例

static resolve(result) {
    // 返回新的promise實(shí)例,執(zhí)行promise實(shí)例中resolve方法
    return new MyPromise(resolve => {
        resolve(result)
    })
}

reject方法

像resolve方法一樣,只不過它接收的是失敗的函數(shù)

static reject(reason) {
    // 返回新的promise實(shí)例,執(zhí)行promise實(shí)例中reject方法
    return new MyPromise((_, reject) => {
        reject(reason);
    })
}

done方法

ES6標(biāo)準(zhǔn)入門一書中,對done方法的解釋是這樣的:無論P(yáng)romise對象的回調(diào)鏈以then方法還是catch方法結(jié)尾,只要最后一個方法拋出錯誤,都有可能無法捕獲到。為此,Promise提供了一個done方法,它總是處于回掉鏈的尾端,保證拋出任何可能出現(xiàn)的錯誤。

done(resolveFn, rejectFn) {
    this.then(resolveFn, rejectFn)
        .catch(reason => {
            setTimeout(() => {
                throw reason;
            }, 0)
        })
}

它可以接收fulfilled、rejected狀態(tài)的回調(diào)函數(shù),也可以不提供任何參數(shù)。但是無論怎樣,done方法都會捕捉到任何可能出現(xiàn)的錯誤,并向全局拋出

finally方法

finally方法是無論成功還是失敗都會執(zhí)行的方法,像這樣的方法還有小程序中的complete方法等等,我們來嘗試實(shí)現(xiàn)一下:

finally(finallyFn) {
    let P = this.constructor;
    return this.then(
        value => P.resolve(finallyFn()).then(() => value),
        reason => P.reject(finallyFn()).then(() => reason)
    )
}

我們來驗證一下

new MyPromise((resolve, reject) => {
    reject('失敗了,我好委屈,嗚嗚嗚~~');
    resolve('已經(jīng)失敗了~~~');
}).then(res => {
    console.log(res);
}, err => {
    console.log(err, 'error');          // 失敗了,我好委屈,嗚嗚嗚~~ error
    return '我要發(fā)奮圖強(qiáng),不會被困難所擊倒,我要成功!??!'
}).finally(() => {
    console.log('執(zhí)行了嗎');            // 這里會輸出"執(zhí)行了嗎"
})

all方法

all方法接收一個數(shù)組,當(dāng)數(shù)組中每個實(shí)例都成功時才會返回,返回的也是一個數(shù)組,每個參數(shù)為對應(yīng)的promise返回的結(jié)果,如果有一項失敗了,all方法都會返回失敗

// 接收數(shù)組參數(shù)
static all(promiseList) {
    // 返回新實(shí)例,調(diào)用后還可使用then、catch等方法
    return new MyPromise((resolve, reject) => {
        let index = 0,      // 成功次數(shù)計數(shù)
            results = [];   // 返回的結(jié)果
        
        for(let i = 0; i < promiseList.length; i++) {
            let item = promiseList[i];
            
            // 如果item不是promise實(shí)例
            if(!(item instanceof MyPromise)) return;
            
            item.then(result => {
                index++;
                results[i] = result;
                if(index === promiseList.length) {
                    resolve(results);
                }
            }).catch(reason => {
                reject(reason);
            })
        }
    })
}

來驗證一下

// 1.有失敗的情況
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.resolve(3);
MyPromise.all([p1, p2, p3])
    .then(res => {
        console.log(res);
    }).catch(err => {
        console.log(err, 'err');     // 2 "err"
    })
// 2.無失敗的情況
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.resolve(2);
let p3 = MyPromise.resolve(3);
MyPromise.all([p1, p2, p3])
    .then(res => {
        console.log(res, 'success');   // [1, 2, 3] "success"
    }).catch(err => {
        console.log(err, 'err');
    })

race方法

race方法同樣接收一個數(shù)組參數(shù),里面每一項是Promise實(shí)例,它返回最快改變狀態(tài)的Promise實(shí)例方法的結(jié)果

static race(promiseList) {
    return new MyPromise((resolve, reject) => {
        promiseList.forEach(item => {
            if(!(item instanceof MyPromise)) return;
            
            item.then(result => {
                resolve(result);
            }).catch(err => {
                reject(err)
            })
        })
    })
}
復(fù)制代碼驗證
// 1.
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res);            // 1 'success'
    }).catch(err => {
        console.log(err, 'err');    
    })
// 2.
let p1 = MyPromise.reject(1);
let p2 = MyPromise.resolve(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res, 'success');   
    }).catch(err => {
        console.log(err, 'err');       // 1 'err'
    })
    
// 3.
let p1 = MyPromise.reject(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.reject(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res, 'success');   
    }).catch(err => {
        console.log(err, 'err');       // 1 'err'
    })

嘗試實(shí)現(xiàn)allSettled方法

allSettled方法也是接收數(shù)組參數(shù),但是它無論成功或者失敗,都會返回

static allSettled(promiseList) {
    return new MyPromise((resolve, reject) => {
        let results = [];
        
        for(let i = 0; i < promiseList.length; i++) {
            let item = promiseList[i];
            
            if(!(item instanceof MyPromise)) return;
            
            item.then(result => {
                results[i] = result;
            }, reason => {
                results[i] = reason;
            })
            resolve(results);
        }
    })
}
復(fù)制代碼驗證
// 1.
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.resolve(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res);            // [1, 2, 3] 'success'
    }).catch(err => {
        console.log(err, 'err');    
    })
// 2.
let p1 = MyPromise.reject(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.reject(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res, 'success');   // [1, 2, 3] 'success'
    }).catch(err => {
        console.log(err, 'err');       
    })
    
// 3.
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res, 'success');   // [1, 2, 3] 'success'
    }).catch(err => {
        console.log(err, 'err');       
    })

以上就是Javascript創(chuàng)建Promise的方法,代碼詳細(xì)清楚,如果在日常工作遇到這個問題,希望你能通過這篇文章解決問題。如果想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI