React Native

JavaScript:Promise基本概念学习

2017-04-20  本文已影响204人  松哥888

什么是Promise

基本概念

var promise = new Promise(function(resolve, reject) {
    // 异步处理
    // 处理结束后、调用resolve 或 reject
});
promise.then(onFulfilled, onRejected);
promise-states.png

基本流程

  1. new Promise(fn)返回一个promise对象
  2. fn中指定异步等处理
    处理结果正常的话,调用resolve(处理结果值);
    处理结果错误的话,调用reject(Error对象);
function getURL(URL) {
    return new Promise(function (resolve, reject) {
        var req = new XMLHttpRequest();
        req.open('GET', URL, true);
        req.onload = function () {
            if (req.status === 200) {
                resolve(req.responseText);
            } else {
                reject(new Error(req.statusText));
            }
        };
        req.onerror = function () {
            reject(new Error(req.statusText));
        };
        req.send();
    });
}
// 运行示例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){
    console.log(value);
}).catch(function onRejected(error){
    console.error(error);
});

异步执行顺序

在使用Promise.resolve(value)等方法的时候,如果promise对象立刻就能进入resolve状态的话,那么你是不是觉得.then里面指定的方法就是同步调用的呢?
实际上,.then中指定的方法调用是异步进行的。下面是一个例子:

var promise = new Promise(function (resolve){
    console.log("inner promise"); // 1
    resolve(42);
});
promise.then(function(value){
    console.log(value); // 3
});
console.log("outer promise"); // 2

输出结果:

inner promise // 1
outer promise // 2
42            // 3

明明可以以同步方式进行调用的函数,非要使用异步的调用方式,这是在Promise设计上的规定方针。

不要对异步回调函数进行同步调用 Promise是符合这一点的。

方法链的形式

Promise里可以将任意个方法连在一起作为一个方法链(method chain)

aPromise.then(function taskA(value){
// task A
}).then(function taskB(vaue){
// task B
}).catch(function onRejected(error){
    console.log(error);
});

如果把在then 中注册的每个回调函数称为task的话,那么我们就可以通过Promise方法链方式来编写能以taskA → task B这种流程进行处理的逻辑了。

// 1: 对同一个promise对象同时调用 `then` 方法
var aPromise = new Promise(function (resolve) {
    resolve(100);
});
aPromise.then(function (value) {
    return value * 2;
});
aPromise.then(function (value) {
    return value * 2;
});
aPromise.then(function (value) {
    console.log("1: " + value); // => 100
})
// 这种是过程式调用,没有顺序执行的功能,每个函数的参数value都是100,需要极力避免这种调用
// 2: 对 `then` 进行 promise chain 方式进行调用
var bPromise = new Promise(function (resolve) {
    resolve(100);
});
bPromise.then(function (value) {
    return value * 2;
}).then(function (value) {
    return value * 2;
}).then(function (value) {
    console.log("2: " + value); // => 100 * 2 * 2 = 400
});
// 这种链式调用,才是期望的样子。
// 就算是简单的值(这里是100),在传递的时候,传的也是promise对象,中间有“装箱”,“拆箱”的操作。
// then函数,就是那种输入输出都是“类型”的函数,能够形成链式调用。所以说promise是monad的一种实现
function badAsyncCall() {
    var promise = Promise.resolve();
    promise.then(function() {
        // 任意处理
        return newVar;
    });
    return promise;
}

promise.then 中产生的异常不会被外部捕获,也不能得到then 的返回值,即使其有返回值。原因是promise !== promise.then()

function anAsyncCall() {
    var promise = Promise.resolve();
    return promise.then(function() {
        // 任意处理
        return newVar;
    });
}

多个异步调用进行统一处理

// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
var startDate = Date.now();
// 所有promise变为resolve后程序退出
Promise.all([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (values) {
    console.log(Date.now() - startDate + 'ms');
    // 约128ms
    console.log(values);    // [1,32,64,128]
});
// 输出:有调度损耗,线程间同步损耗,理论上应该输出128ms,实际输出145ms是合理的。多执行几次,这个值每次都会变
// 145ms
// [ 1, 32, 64, 128 ]
  • 4个promise是并行执行的
  • 4个promise都执行完毕后再执行then
  • 传递的values数组和promise数组的顺序一致
// `delay`毫秒后执行resolve
function timerPromisefy(delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
// 任何一个promise变为resolve或reject 的话程序就停止运行
Promise.race([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (value) {
    console.log(value);    // => 1
});
var winnerPromise = new Promise(function (resolve) {
        setTimeout(function () {
            console.log('winner funtion running');
            resolve('this is winner');
        }, 10);
    });
var loserPromise = new Promise(function (resolve) {
        setTimeout(function () {
            console.log('loser funtion running');
            resolve('this is loser');
        }, 1000);
    });
// 第一个promise变为resolve后程序停止
Promise.race([winnerPromise, loserPromise]).then(function (value) {
    console.log(value);    // => 'this is winner'
});

// 输出:
// winner funtion running
// this is winner
// loser funtion running
  • 数组中[winnerPromise, loserPromise]中的promise同时开始执行
  • 10mswinnerPromise胜出,输出winner funtion running
  • Promise.race()函数有了结果,就是winnerPromise,顺序执行then,输出传递的value,这里是this is winner。这个就是winnerPromiseresolve信息resolve('this is winner');
  • loserPromise对象没有被取消,继续执行。1000ms后,loserPromise对象中的函数执行,输出loser funtion running
    *loserPromise对象中的resolve信息'resolve('this is loser');不会被传递。

错误处理

then_catch.png
function throwError(value) {
    // 抛出异常
    throw new Error(value);
}
// <1> onRejected不会被调用
function badMain(onRejected) {
    return Promise.resolve(42).then(throwError, onRejected);
}
// <2> 有异常发生时onRejected会被调用
function goodMain(onRejected) {
    return Promise.resolve(42).then(throwError).catch(onRejected);
}
// <3> 使用下一级的then
function nextThenMain(onRejected) {
    return Promise.resolve(42).then(throwError).then(null, onRejected);
}

// 运行示例
badMain(function(){
    console.log("BAD");
});
goodMain(function(){
    console.log("GOOD");
});
nextThenMain(function(){
    console.log("Next Then");
});
// 输出: 
// GOOD
// Next Then

这里的badMain,之所以捕获不到错误,是因为用了同一级的.then
错误处理统一使用.catch,不要用.then

// 不要用throw,这虽然能运行,但很不promise风格,要避免
var promise = new Promise(function(resolve, reject){
    throw new Error("message");
});
promise.catch(function(error){
    console.error(error);// => "message"
});
// 这是推荐的方式,形成习惯
var promise = new Promise(function(resolve, reject){
    reject(new Error("message"));
});
promise.catch(function(error){
    console.error(error);// => "message"
})
var onRejected = console.error.bind(console);
var promise = Promise.resolve();
promise.then(function () {
    return Promise.reject(new Error("this promise is rejected"));
}).catch(onRejected);
//  this promise is rejected

参考文章

JavaScript Promise迷你书(中文版)

ES6 JavaScript Promise的感性认知

上一篇 下一篇

猜你喜欢

热点阅读