js异步编程(updating)

js 异步编程方式: Promise,generator/yield,async/await

generator()

  • Generator函数是一个状态机,封装了多个内部状态。
  • Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
  • Generator函数可以暂停执行的function。

函数名之前要加星号区别,只有调用next方法时,函数f才会执行

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
// yield是暂停标志
}

done属性的值false,表示遍历还没有结束。//ture 相反

async await

promise

什么是Promise

Promise最早由社区提出并实现,典型的一些库有Q,when, bluebird等;它们的出现是为了更好地解决JavaScript中异步编程的问题,传统的异步编程最大的特点就是地狱般的回调嵌套,一旦嵌套次数过多,就很容易使我们的代码难以理解和维护。而Promise则可以让我们通过链式调用的方法去解决回调嵌套的问题,使我们的代码更容易理解和维护,而且Promise还增加了许多有用的特性,让我们处理异步编程得心应手。

如何创建Promise

ES6给我们提供了一个原生的构造函数Promise,我们可以看一下这个构造函数:

// 下面的代码可以直接运行在浏览器的控制台中(Chrome浏览器)

> typeof Promise
"function" // 可以看出这是一个构造函数
> Promise
function Promise() { [native code] } // ES6的原生支持

下面我们先来创建一个promise,下面是一个简单的示例:

// 我们先使用ES5的语法
var promise = new Promise(function(resolve, reject) {
    var flag = Math.random();
    setTimeout(function() {
        if(flag) {
            resolve('success');
        }
        else {
            reject('fail');
        }
    }, 1000);
});
console.log(promise); // 在浏览器的控制台运行的话,它返回的是一个包含了许多属性的Promise对象;在Node.js环境中控制台输出 Promise { <pending> }。

promise.then(function(result) {
    console.log(result);
}, function(err) {
    console.log(err);
}); // 1s后这里的输出可能是fail也可能是success

下面来解释一下上面的代码:
因为Promise是一个构造函数,所以我们使用了new操作符来创建promise。
构造函数Promise的参数是一个函数(暂时叫它func),这个函数(func)有两个参数resolvereject,它们分别是两个函数,这两个函数的作用就是将promise的状态从pending(等待)转换为resolved(已解决)或者从pending(等待)转换为rejected(已失败)。
创建后的promise有一些方法,thencatch。当然我们也可以人为的在Promise函数上添加一些满足我们自己需求的方法,方便每一个promise对象使用。

如果我们使用一些ES6的语法的话,我们上面的代码会更加简洁:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        Math.random() > 0.5 ? resolve('success') : reject('fail');
    }, 1000)
});

console.log(p);

p.then((result) => {
    console.log(result);
}, (err) => {
    console.log(err);
});

其实可以这样理解,Promise函数体的内部包裹着一个异步的请求或者操作或者函数;然后我们可以在这个异步的操作完成的时候使用resolve函数将我们获得的结果传递出去,或者使用reject函数将错误的消息传递出去。

Promise对象的一些方法

Promise对象可以通过使用then方法将上一步返回的结果获取过来(不管是resolved还是rejected),可以通过使用catch方法捕获Promise对象在使用catch之前的异常。

首先来说一下then方法的使用:

let p = new Promise((resolve, reject) => {
   let flag = Math.random() > 0.5 ? true : false;
   if(flag) {
       console.log('使用resolve将promise状态从pending变为resolved');
       resolve('success');
   }
   else {
       console.log('使用reject将promise状态从pending变为rejected');
       reject('fail');
   }
});

// @1
p.then((result) => {
    console.log('接受resolved的结果');
    console.log(result);
}, (err) => {
    console.log('捕获错误的结果');
    console.log(err);
});

我们可以看到,then方法可以接受两个函数作为参数,第一个函数是用来处理resolve的结果,第二个是可选的,用来处理reject的结果。也就是说,我们在创建p这个Promise对象的时候,通过函数resolve传递出去的结果可以被p的第一个then方法中的第一个函数捕获然后作为它的参数。通过函数reject传递出去的结果可以被p的第一个then方法中的第二个函数捕获然后作为它的参数。

当然我们还可以在每一个then方法中创建新的Promise,然后将这个Promise对象返回,之后我们就可以在后面的then方法中继续对这个对象进行操作。下面是一个简单的例子:

let p1 = new Promise((resolve, reject) => {
    let flag = Math.random() > 0.5 ? true : false;
    resolve();
});
// @2 使用then方法进行链式的调用
p1.then(() => {
    return 1;
}).then((result) => {
    console.log(result);
    return 'hello'
}).then((result) => {
    console.log(result);
});

// @3 在then方法内部可以再次使用异步的操作
p1.then(() => {
    console.log('******');
    let p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(123);
        }, 1000);
    });
    return p1;
}).then((result) => {
    console.log(result);
});

从上面的代码中我们可以看到,一旦创建一个Promise对象之后,我们就可以使用then方法来进行链式的调用,而且我们可以把每一次的结果都返还给下一个then方法,然后在下一个then方法中对这个值进行处理。每一个then方法中都可以再次新创建一个Promise对象,然后返还给下一个then方法处理。

Promise还有另一个方法catch,这个方法其实是then方法的一种特例,这个特例就是:

.then(null, rejection)
相当于我们不使用then方法的第一个函数,只是用第二个函数;catch函数比较简单,就是用来捕获之前的then方法里面的异常,我们可以简单的来看一个例子:

let p = new Promise((resolve, reject) => {
resolve();
});
p.then(() => {
console.log('progress...');
}).then(() => {
throw new Error('fail');
}).catch((err) => {
console.log(err);
});
上面代码的输出结果如下:

progress...
VM141:9 Error: fail(…)
我们可以使用catch函数来捕获整个then函数链中的异常。

Promise的一些方法

Promise.all方法用来包装许多个Promise实例,然后组成了一个新的Promise对象,新的Promise对象的状态由前面几个被包裹的Promise对象的状态决定,如果前面的Promise都被resolve了,那么新的Promise的状态也是resolve的;只要有一个Promise被reject了,那么组成的新的Promise的状态也是reject的。

可以看下面一个例子:

let arr = [1, 2, 3].map(
    (value) => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(value);
            }, value * 1000);
        });
    }
);

console.log(arr);

let promises = Promise.all(arr)
.then((result) => {
    console.log(result);
}).catch((err) => {
    console.log(err);
});

上面的代码的输出结果如下:

[ Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> } ]
[ 1, 2, 3 ] // 3s后输出的结果

Promise.race方法和上面的Promise.all有点类似,都是包装许多的Promise对象,然后组成了一个新的Promise对象,但是使用Promise.race的含义是:只要包裹的的Promise对象中有一个的状态发生了改变,那么组成的这个新的Promise对象的状态就是上面那个率先改变的Promise实例的状态。

下面是一个简单的例子:

let arr = [1, 2, 3].map(
    (value) => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(value);
            }, value * 1000);
        });
    }
);

console.log(arr);

let promises = Promise.race(arr)
    .then((result) => {
        console.log(result);
    }).catch((err) => {
        console.log(err);
    });
上面程序的输出结果如下:

[ Promise { <pending> },
  Promise { <pending> },
  Promise { <pending> } ]
1 // 是最先改变状态的那个Promise实例resolve的值

Promise.resolve方法主要是将一个值转变为一个Promise对象,然后使它具有Promise的一些方法和特性,为了满足我们一些特殊情况下的要求。

下面是一个简单的例子:

let arr = [null, 0, 'hello',
    { then: function() { console.log(' a thenable obj')}}
];

arr.map((value) => {
        return Promise.resolve(value);
    });

console.log(arr);
上面的输出结果如下:

[ null, 0, 'hello', { then: [Function: then] } ]
 a thenable obj // Promise.resolve方法会将具有then方法的对象转换为一个Promise对象,然后就立即执行then方法。

Promise.reject方法和Promise.resolve方法一样,只不过通过Promise.reject方法产生的Promise对象的状态是rejected的,下面是一个示例:

let p = Promise.reject('fail');
p.catch((err) => {
    console.log(err);
}); // fail

async await
fetch 介绍

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

推荐阅读更多精彩内容