Javascript學(xué)習(xí)筆記-Promise

眾所周知,JS存在一個(gè)設(shè)計(jì)缺陷,就是所有代碼都為單線程執(zhí)行,所以JS的所有網(wǎng)絡(luò)操作、瀏覽器事件都必須異步執(zhí)行,通過(guò)回調(diào)函數(shù)實(shí)現(xiàn):

request.onreadystatechange = function () {
    if (request.readyState === 4) {
        if (request.status === 200) {
            return success(request.responseText);
        } else {
            return fail(request.status);
        }
    }
}

// 另一種寫(xiě)法
var ajax = ajaxGet('http://...');
ajax.ifSuccess(success)
    .ifFail(fail);

這種鏈?zhǔn)綄?xiě)法的好處在于先統(tǒng)一執(zhí)行AJAX邏輯,不關(guān)心如何處理結(jié)果,然后根據(jù)結(jié)果的成功與否,在將來(lái)的某個(gè)時(shí)候調(diào)用success函數(shù)或fail函數(shù)。這種“承諾將來(lái)會(huì)執(zhí)行”的對(duì)象在JS中稱(chēng)為Promise對(duì)象,Promise有各種開(kāi)源實(shí)現(xiàn),但在ES6中被統(tǒng)一規(guī)范,由瀏覽器直接支持。
我們先看一個(gè)最簡(jiǎn)單的Promise例子:生成一個(gè)0-2之間的隨機(jī)數(shù),如果小于1,則等待一段時(shí)間后返回成功,否則返回失敗:

function test(resolve, reject) {
    var timeOut = Math.random() * 2;
    log('set timeout to: ' + timeOut + ' seconds.');
    setTimeout(function () {
        if (timeOut < 1) {
            log('call resolve()...');
            resolve('200 OK');
        }
        else {
            log('call reject()...');
            reject('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);
}

這個(gè)test()函數(shù)有兩個(gè)參數(shù),這兩個(gè)參數(shù)都是函數(shù),如果執(zhí)行成功,我們將調(diào)用resolve('200 OK'),如果執(zhí)行失敗,我們將調(diào)用reject('timeout in ' + timeOut + ' seconds.')??梢钥闯?,test()函數(shù)只關(guān)心自身的邏輯,并不關(guān)心具體的resolve和reject將如何處理結(jié)果。我們可以用一個(gè)Promise對(duì)象來(lái)執(zhí)行它,并在將來(lái)某個(gè)時(shí)刻獲得成功或失敗的結(jié)果:

var p1 = new Promise(test);
var p2 = p1.then(function (result) {
    console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) {
    console.log('失?。? + reason);
});

// 串聯(lián)起來(lái)
new Promise(test).then(function (result) {
    console.log('成功:' + result);
}).catch(function (reason) {
    console.log('失?。? + reason);
});

// 清除log:
var logging = document.getElementById('test-promise-log');
while (logging.children.length > 1) {
    logging.removeChild(logging.children[logging.children.length - 1]);
}

// 輸出log到頁(yè)面:
function log(s) {
    var p = document.createElement('p');
    p.innerHTML = s;
    logging.appendChild(p);
}

new Promise(function (resolve, reject) {
    log('start new Promise...');
    var timeOut = Math.random() * 2;
    log('set timeout to: ' + timeOut + ' seconds.');
    setTimeout(function () {
        if (timeOut < 1) {
            log('call resolve()...');
            resolve('200 OK');
        }
        else {
            log('call reject()...');
            reject('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);
}).then(function (r) {
    log('Done: ' + r);
}).catch(function (reason) {
    log('Failed: ' + reason);
});

可見(jiàn)Promise最大的好處是在異步執(zhí)行的流程中,把執(zhí)行代碼和處理結(jié)果的代碼清晰地分離了。下面的例子演示了如何串行執(zhí)行一系列異步任務(wù):

// ajax函數(shù)將返回Promise對(duì)象:
function ajax(method, url, data) {
    var request = new XMLHttpRequest();
    return new Promise(function (resolve, reject) {
        request.onreadystatechange = function () {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    resolve(request.responseText);
                } else {
                    reject(request.status);
                }
            }
        };
        request.open(method, url);
        request.send(data);
    });
}

var log = document.getElementById('test-promise-ajax-result');
var p = ajax('GET', '/api/categories');
p.then(function (text) { // 如果AJAX成功,獲得響應(yīng)內(nèi)容
    log.innerText = text;
}).catch(function (status) { // 如果AJAX失敗,獲得響應(yīng)代碼
    log.innerText = 'ERROR: ' + status;
});

除了串行執(zhí)行異步任務(wù)外,Promise還可以并行執(zhí)行異步任務(wù),用Promise.all()實(shí)現(xiàn)如下:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同時(shí)執(zhí)行p1和p2,并在它們都完成后執(zhí)行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 獲得一個(gè)Array: ['P1', 'P2']
});

有些時(shí)候,Promise執(zhí)行異步任務(wù)是為了容錯(cuò),比如同時(shí)向兩個(gè)URL讀取用戶(hù)的個(gè)人信息,只需要獲得先返回的結(jié)果即可,用Promise.race()實(shí)現(xiàn):

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});
// 由于p1執(zhí)行較快,Promise的then()將獲得結(jié)果'P1'。p2仍在繼續(xù)執(zhí)行,但執(zhí)行結(jié)果將被丟棄。

如果我們組合使用Promise,就可以把很多異步任務(wù)以并行和串行的方式組合起來(lái)執(zhí)行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Promise簡(jiǎn)介 Promise是ES6新引入的對(duì)象,是新增加的異步處理手段。在Javascript 中在...
    Patrick浩閱讀 244評(píng)論 0 0
  • Promiese 簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果,語(yǔ)法上說(shuō),Pr...
    雨飛飛雨閱讀 3,491評(píng)論 0 19
  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,270評(píng)論 0 16
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 11,133評(píng)論 26 95
  • 弄懂js異步 講異步之前,我們必須掌握一個(gè)基礎(chǔ)知識(shí)-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,886評(píng)論 0 5

友情鏈接更多精彩內(nèi)容