JavaScript Promise介绍

JavaScript Promise介绍

前言

众所周知,在JavaScript的世界中,代码都是单线程执行的。由于这个原因,JavaScript中的耗时操作,如网络操作、浏览器事件等,都需要异步执行。这也导致在JavaScript中异步操作是非常频繁且常见的。

异步概念

在执行某些耗时、不会立即返回结果的操作时,不会阻塞后面的操作,一旦该耗时操作完成时,立即通知需要调用其结果的函数来做后续处理。

简单来理解就是:同步按照写的代码顺序执行,异步不按照代码顺序执行

回调函数进行异步操作

和同步操作不同,异步操作是不会立即返回结果的(如发起网络请求,下载文件,操作数据库等)。如果我们后续的函数需要之前返回的结果,又怎样使之前的异步操作在其完成时通知到后续函数来执行呢?

通常,我们可以将这个函数先定义,存储在内存中,将其当做参数传入之前的异步操作函数中,等异步操作结束,就会调用执行这个函数,这个函数就叫做回调函数(callback)。

举个栗子:

// 下载
function download(callback){
    // 模拟异步操作
    setTimeout(function(){
        // 调用回调函数
        callback('下载完成');
    }, 1000);
}

function callback(value){
    // 下载完成的处理
    console.log(value);
}

download(callback);

// 这段代码将在1秒后在控制台打印“下载完成”

但假如callback函数同样是个异步函数,且callback里又嵌入了callback呢? 例如需求是等待第一个文件下载完成后,再下载第二个文件,等待第二个文件下载完成后,再下载第三个文件...,这样的话,上面这种方法就不可取了,因为会产生很多的函数嵌套,嵌套太深容易引发回调地狱

Promise进行异步操作

古人云:“君子一诺千金”,这种“承诺将来会执行”的对象,在JavaScript中称为Promise对象。

我们首先看看如何通过Promise改造上面的回调函数异步操作

var download = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        resolve('下载完成');
    }, 1000);
})

download.then(value => {
    console.log(value);
});

// 这段代码将在1秒后在控制台打印“下载完成”

可以看到代码简洁了一些,当然,不只是如此,它还可以连续调用,例如我们上面所说的回调地狱问题,通过Promise可以很简洁的实现

var download1 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('1');
        resolve('文件1下载完成');
    }, 1000);
});

var download2 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('2');
        resolve('文件2下载完成');
    }, 1000);
});

var download3 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('3');
        resolve('文件3下载完成');
    }, 1000);
});

download1.then(download2).then(download3);

在download1完成之后会调用download2,download2完成之后会调用download3,实现任务串行,为此,对于那些需要连续执行的异步操作,Promise可以是一种很好的解决办法。

Promise相对于callback,具有更优的代码流,并且具有很好的灵活性。Promise符合自然的事物执行顺序,即先做异步操作,然后再用.then告知下一步该做什么。而在Callback的用法中,先得知道下一步做什么,然后才能将其作为callback函数传入异步操作函数中。

认识Promise

Promise的创建

Promise创建时,会传给promise一个称为excutor执行器的函数。这个excutor我们可以理解为生产者的生产过程函数。这个函数含有两个参数resolvereject,这俩参数也同样是函数,用来传递异步操作的结果

let promise = new Promise(function(resolve, reject) {
  // executor 
})

有几点值得说一下:

  1. 在Promise对象创建时,excutor会立即执行。
  2. resolvereject是JS引擎自动创建的函数,我们无需自己创建,只需将其作为参数传入就好。

Promise的状态与执行流程

Promise执行流程图如下:

image-20210719192052771.png

创建的promise的内部状态是个对象,初始时为:

{
   state,  //pending
   result,  //undefined
}

当Promise中的异步任务执行完,要么产生value,要么产生error,此时会立即调用resolve(当产生value时)或者调用reject(当产生error)时,内部状态也会随之改变,如下图所示:

image-20210719191048764.png

注意,当excutor里面即使调用了多个resolvereject,其最终还是只执行一个,其他的都被忽略掉。

let promise = new Promise(function(resolve, reject) {
  resolve("done");

  reject(new Error("…")); // ignored
  setTimeout(() => resolve("…")); // ignored
});

多任务并行

除了串行执行若干异步任务外,Promise还可以并行执行异步任务。

如果一个页面,需要从多个接口下载文件,但假如这些接口之间没有依从性,此时我们可以让多个任务并行以提升效率。

var download1 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('1');
        resolve('文件1下载完成');
    }, 500);
});

var download2 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('2');
        resolve('文件2下载完成');
    }, 800);
});

var download3 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('3');
        resolve('文件3下载完成');
    }, 1000);
});

Promise.all([download1, download2, download3]).then(function (results) {
    console.log(results); // 返回一个数组,包含三个异步任务的执行结果
});

多任务竞争

任务之间是竞争关系,使用Promise也可以很简单的实现。

如果一个页面,需要从多个接口下载文件,但假如只要其中一个任务执行成功获取其结果即可,其它任务便可丢弃。

var download1 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('1');
        resolve('文件1下载完成');
    }, 500);
});

var download2 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('2');
        resolve('文件2下载完成');
    }, 800);
});

var download3 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('3');
        resolve('文件3下载完成');
    }, 1000);
});

Promise.race([download1, download2, download3]).then(function (results) {
    console.log(results); // 返回download1的执行结果
});

由于download1执行较快,所以在then中将获得download1的结果,但是download2与download3任然会继续执行,只是执行的结果将会被丢弃。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,681评论 6 517
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,205评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 169,421评论 0 362
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,114评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,116评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,713评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,170评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,116评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,651评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,714评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,865评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,527评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,211评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,699评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,814评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,299评论 3 379
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,870评论 2 361

推荐阅读更多精彩内容