Promise學(xué)習總結(jié)

了解promise誕生的歷史背景


1.詞語本意解釋

promise [?prɑ:m?s] 許諾;允諾缩筛;(有可能)
從字面意思消略,表示一個將來的狀態(tài)。
用在異步操作里面瞎抛,幫我們?nèi)ヌ幚硪患磥砜赡馨l(fā)生的一件什么事艺演。

2.MDN解釋
  • promise對象用于異步計算
  • 一個promise表示一個現(xiàn)在、將來或永遠不可能可用的值。
3.按照用途來解釋
  • 主要用來異步計算
  • 可以將異步操作隊列化胎撤,按照期望的順序執(zhí)行晓殊,返回符合預(yù)期的結(jié)果。
  • 可以在對象之間傳遞和操作promise伤提,幫助我們處理隊列巫俺。
4.為什么會有Promise?
  • JavaScript包含大量異步操作
    • JavaScript為檢查表單而生肿男。
    • 創(chuàng)造它的首要目標是操作DOM
    • 所以介汹,JavaScript的操作大多是異步的。
5.為什么異步操作可以避免界面凍結(jié)呢舶沛?
  • 假設(shè)你去到一家飯店嘹承,自己找座坐下了,然后招呼服務(wù)員拿菜單如庭。
  • 服務(wù)員說:“對不起叹卷,我是同步服務(wù)員,我要服務(wù)完這張桌子才能招呼你坪它≈柚瘢”
  • 你是不是很想抽ta?
  • 那一桌人明明已經(jīng)吃上了,你只是想要菜單哟楷,這么小的一個動作瘤载,服務(wù)員卻要你等到別人的一個大動作完成。
  • 這就是同步的問題: 順序交付的工作1234卖擅,必須按照1234的順序完成鸣奔。
  • 異步,則是將耗時很長的A交付的工作交給系統(tǒng)之后惩阶,就去繼續(xù)做B交付的工作挎狸。等到系統(tǒng)完成前面的工作之后,再通過回調(diào)或者事件断楷,繼續(xù)做A剩下的工作锨匆。
  • 從觀察者的角度看起來,AB工作的完成順序冬筒,和交付他們的時間順序無關(guān)恐锣,所以叫"異步"。
6.異步操作的常見語法
  • 事件偵聽與響應(yīng)
document.getElementById('start').addEventListener('click', start, false);

function start() {
    // 響應(yīng)事件舞痰,進行相應(yīng)的操作
}

// jQuery 用 `.on()` 也是事件偵聽
$('#start').on('click', start);
  • 回調(diào)
// 比較常見的有ajax
$.ajax('http://baidu.com', {
    success: function (res) {
        // 這里就是回調(diào)函數(shù)了
    }
});

// 或者在頁面加載完畢后回調(diào)
$(function(){
    // 這里也是回調(diào)函數(shù)
});
7.瀏覽器中的JavaScript
  • 異步操作以事件為主土榴,如ready、onload
  • 回調(diào)主要出現(xiàn)在Ajax和file API
    這個時候問題尚不算嚴重
8.有了Node.js之后

對異步的依賴進一步加劇了...
Node.js出現(xiàn)了的時候响牛,PHP玷禽、JAVA赫段、Python這些服務(wù)器端的語言都已經(jīng)很成熟了。Node.js作為一個后來者矢赁,想要從中分一杯羹糯笙,必須有自己的絕活。

  • 無阻塞高并發(fā)撩银,是Node.js的招牌
  • 異步操作是其保障给涕。
  • 大量操作依賴回調(diào)函數(shù)。
9.使用Node.js開發(fā)時的問題
  • 不好維護
    稍有不慎蜒蕾,就會踏入“回調(diào)地獄”
a(function (resultsFromA) {
    b(resultsFromA, function (resultsFromB) {
        c(resultsFromB, function (resultsFromC) {
            d(resultsFromC, function (resultsFromD) {
                e(resultsFromD, function (resultsFromE) {
                    f(resultsFromE, function (resultsFromF) {
                        console.log(resultsFromF);
                    })
                })
            })
        })
    })
});
  • 除此之外稠炬,還有更深層次的問題
    遍歷目錄焕阿,找出最大的一個文件咪啡。
這段代碼跑在node環(huán)境中,使用一些node模塊暮屡。
const fs = require('fs');  //文件系統(tǒng)
const path = require('path');

function findLargest(dir, callback) {
    fs.readdir(dir, function (err, files) {
    //用fs.readdir讀文件夾的內(nèi)容撤摸。傳入回調(diào)函數(shù)
        if (err) return callback(err); // [1]
        //如果發(fā)現(xiàn)錯誤,就用回調(diào)函數(shù)傳出錯誤

        //如果讀出來就執(zhí)行下面操作
        let count = files.length; // [2]
        //取出來文件數(shù)量
        let errored = false;
        let stats = [];
        files.forEach( file => {  //對文件進行遍歷
            fs.stat(path.join(dir, file), (err, stat) => {
            //每一個文件都用fs.stat去取狀態(tài)褒纲。取出狀態(tài)用到回調(diào)函數(shù)
                if (errored) return; // [1]
                //發(fā)生錯誤准夷,中斷,return掉
                if (err) {
                    errored = true;
                    return callback(err);
                }
                stats.push(stat); // [2]
                //沒有發(fā)生錯誤莺掠,把stat文件放入數(shù)組

                if (--count === 0) {  //用count變量來計數(shù)衫嵌,每讀完一個文件,都減一彻秆,當?shù)?的時候楔绞,就認為所有文件都讀完了
                    let largest = stats  //然后對數(shù)組進行遍歷,找出其中
                        .filter(function (stat) { return stat.isFile(); })
                        .reduce(function (prev, next) {
                            if (prev.size > next.size) return prev;
                            //找出其中最大的文件信息唇兑,用下面的callback返回
                            return next;  //記下內(nèi)容
                        });
                    callback(null, files[stats.indexOf(largest)]);
                    //第一個參數(shù)酒朵,是否有參數(shù),因為是正常的扎附,所以返回null
                }
            });
        });
    });
}

findLargest('./path/to/dir', function (err, filename) {
//使用的時候調(diào)用蔫耽,'傳入要查找的目錄',傳入回調(diào)函數(shù)
//回調(diào)函數(shù)接受兩個參數(shù)×粢梗“錯誤”匙铡,“文件名”
    if (err) return console.error(err);  
    //如果有錯誤,就返回錯誤信息碍粥。
    console.log('largest file was:', filename);
    //沒有錯誤鳖眼,返回文件名
});
//在標記的地方有問題,以后補充問題

異步回調(diào)有四個問題

  • 嵌套層次很深即纲,難以維護
  • 無法正常使用return 和 throw
  • 無法正常檢索堆棧信息
  • 多個回調(diào)之間難以建立聯(lián)系

Promise入門

1.Promise簡介
new Promise(
    /* 執(zhí)行器 executor */
    function (resolve, reject) {
        // 一段耗時很長的異步操作
        resolve(); // 數(shù)據(jù)處理完成
        reject(); // 數(shù)據(jù)處理出錯
    }
)
    .then(function A() {
        // 成功具帮,下一步
    }, function B() {
        // 失敗博肋,做相應(yīng)處理
    });
2.Promise詳解
  • Promise是一個代理對象,它和原先要進行的操作并無關(guān)系蜂厅。
  • 它通過引入一個回調(diào)匪凡,避免更多的回調(diào)。
3.Promise有三個狀態(tài):
  • pending [待定] 初始狀態(tài)
  • fulfilled [實現(xiàn)] 操作成功
  • rejected [被否決] 操作失敗

Promise狀態(tài)發(fā)生改變掘猿,就會觸發(fā).then()里的響應(yīng)函數(shù)處理后續(xù)步驟
Promise狀態(tài)一經(jīng)改變病游,不會再變。


4.范例
  • 定時執(zhí)行
console.log('here we go');
new Promise( resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
    .then( value => {
        console.log( value + ' world');
    });

//輸出結(jié)果
here we go
hello world  //2秒之后輸出
5.兩步執(zhí)行范例
  • 分兩次稠通,順序依次執(zhí)行
console.log('here we go');
new Promise( resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
    .then( value => {
        console.log(value);
        return new Promise( resolve => {
            setTimeout( () => {
                resolve('world');
            }, 2000);
        });
    })
    .then( value => {
        console.log( value + ' world');
    });

//輸出結(jié)果
here we go
hello  //2秒
world world  //2秒
6.已完成的Promise衬衬,再執(zhí)行.then()
console.log('start');

let promise = new Promise(resolve => {
    setTimeout(() => {
        console.log('the promise fulfilled');
        resolve('hello, world');
    }, 1000);
});

setTimeout(() => {
    promise.then( value => {
        console.log(value);
    });
}, 3000);

//輸出結(jié)果
start
the promise fulfilled  //1秒之后輸出
hello, world  //再過2秒之后輸出
7.在.then()的函數(shù)里面不返回新的Promise,會怎么樣改橘?
console.log('here we go');
new Promise(resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
    .then( value => {
        console.log(value);
        console.log('everyone');
        (function () {
            return new Promise(resolve => {
                setTimeout(() => {
                    console.log('Mr.Laurence');
                    resolve('Merry Xmas');
                }, 2000);
            });
        }());
        return false;  //沒有等待里面的promise執(zhí)行
        //但是里面的定時器仍在等待執(zhí)行
        //在promise里面如果不直接返回一個promise實例
        //就會默認執(zhí)行下一個環(huán)節(jié)
        //即使返回false也不影響下一組滋尉,false作為直接傳遞到下一組
    })
    .then( value => {
        console.log(value + ' world');
    });

//輸出結(jié)果
here we go  //先輸出

hello  //這三個同時輸出
everyone
false worl

Mr.Laurence  //最后輸出這個
8.then()
  • .then()接受兩個函數(shù)作為參數(shù),分別代表fulfilled和rejected
  • .then() 返回一個新的Promise實例飞主,所以它可以鏈式調(diào)用
  • 當前面的Promise狀態(tài)改變時狮惜,.then()根據(jù)其最終狀態(tài),選擇特定的狀態(tài)響應(yīng)函數(shù)執(zhí)行碌识。
  • 狀態(tài)響應(yīng)函數(shù)可以返回新的promise碾篡,或其他值
  • 如果返回新的Promise,那么下一級.then()會在新Promise狀態(tài)改變之后執(zhí)行筏餐。
  • 如果返回其他任何值开泽,則會立刻執(zhí)行下一級.then()

小測試

1. .then()里有.then()的情況
  • 因為.then()返回的還是Promise實例
  • 會等里面的.then()執(zhí)行完,在執(zhí)行外面的魁瞪。
  • 對于我們來說穆律,此時最好將其展開,會更好讀佩番。
console.log('start');
new Promise( resolve => {
    console.log('Step 1');
    setTimeout(() => {
        resolve(100);
    }, 1000);
})
    .then( value => {
        return new Promise(resolve => {
            console.log('Step 1-1');
            setTimeout(() => {
                resolve(110);
            }, 1000);
        })
            .then( value => {
                console.log('Step 1-2');
                return value;
            })
            .then( value => {
                console.log('Step 1-3');
                return value;
            });
    })
    .then(value => {
        console.log(value);
        console.log('Step 2');
    });

//輸出結(jié)果
start
Step 1

Step 1-1
Step 1-2
Step 1-3

Step 2

學(xué)會使用promise解決異步回調(diào)帶來的問題

掌握promise的進階用法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末众旗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子趟畏,更是在濱河造成了極大的恐慌贡歧,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赋秀,死亡現(xiàn)場離奇詭異利朵,居然都是意外死亡,警方通過查閱死者的電腦和手機猎莲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門绍弟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人著洼,你說我怎么就攤上這事樟遣《穑” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵豹悬,是天一觀的道長葵陵。 經(jīng)常有香客問我,道長瞻佛,這世上最難降的妖魔是什么脱篙? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮伤柄,結(jié)果婚禮上绊困,老公的妹妹穿的比我還像新娘。我一直安慰自己适刀,他們只是感情好秤朗,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蔗彤,像睡著了一般川梅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上然遏,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音吧彪,去河邊找鬼待侵。 笑死,一個胖子當著我的面吹牛姨裸,可吹牛的內(nèi)容都是我干的秧倾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼傀缩,長吁一口氣:“原來是場噩夢啊……” “哼那先!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赡艰,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤售淡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后慷垮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體揖闸,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年料身,在試婚紗的時候發(fā)現(xiàn)自己被綠了汤纸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡芹血,死狀恐怖贮泞,靈堂內(nèi)的尸體忽然破棺而出楞慈,到底是詐尸還是另有隱情,我是刑警寧澤啃擦,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布抖部,位于F島的核電站,受9級特大地震影響议惰,放射性物質(zhì)發(fā)生泄漏慎颗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧黍特,春花似錦巩检、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撇眯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虱咧,已是汗流浹背熊榛。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腕巡,地道東北人玄坦。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像绘沉,于是被迫代替她去往敵國和親煎楣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355