Web 前端开发 前端开发那些事让前端飞

Javascript异步编程之Promise

2018-03-07  本文已影响0人  黎贝卡beka

异步回调的问题

Promise的设计

new Promise(
    /* 执行器 executor */
    function (resolve, reject) {
        /* 一段耗时很长的异步操作 */
    resolve(); // 数据处理完成
    reject(); // 数据处理出错
    }
)
.then(function A() {
   // 成功,下一步
 }, function B() {
   // 失败,做相应处理
});
  1. 定时执行
console.log('here we go');
new Promise( resolve => {
    setTimeout( () => {
        resolve('hello');
    }, 2000);
})
.then( value => {
    console.log( value + ' world');
});

输出结果:
here we go
Promise {<pending>}
(约2s后)hello world

  1. 分两次,依次定时执行
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');
});

输出结果:
here we go
Promise {<pending>}
(约2s后)hello
(约2s后)world world

  1. 对已完成的Promise执行.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);

输出结果:
start
74
(约1s后)the promise fulfilled
(约2s后)hello, world

在任何地方生成一个Promise队列后,可以把它作为一个变量传到其他地方,不管这个Promise状态是否是完成状态,队列都会依次执行;若为完成,会依次执行Promise内的执行器部分,若已完成,后面追加的.then将会得到Promise返回的值

  1. then()里不返回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;
})
.then( value => {
     console.log(value + ' world');
});

输出结果:
here we go
Promise {<pending>}
hello
everyone
false world
(约2s后)Mr.Laurence

在这里,第一个then()里相应函数返回的Promise并不会等待新创建的Promise实例执行(那段立即函数,陷阱!),即使then()响应函数返回值是false;也就是说,在Promise里即使没有直接返回一个Promise实例,也会默认去执行下一个环节。这里将return false注释掉,可以发现,会默认返回undefined值,并传递给下一个then()的相应函数中。

下面介绍一下.then()

避免嵌套.then()

因为.then()返回的还是Promise实例,会等里面的.then()执行完,才执行外面的。对于我们来说,此时最好将其展开,避免then()函数里嵌套then(),也方便阅读。

四道题目

原问题地址译文地址请戳←
假设doSomething和doSomethingElse返回的都是一个Promise实例,下面的四种 promises 的区别是什么?

  1. 问题一
doSomething()
    .then(function () {
        return doSomethingElse();
    })
    .then(finalHandler);

答案:
(最常见的Promise形式,then()相应函数返回一个新的Promise实例)

// doSomething
// |-----------|
//             doSomethingElse(undefined)
//             |------------|
//                          finalHandler(resultOfDoSomethingElse)
//                          |------------|
  1. 问题二
doSomething()
    .then(function () {
        doSomethingElse();
    })
    .then(finalHandler);

答案:
(第一个then()响应函数没有直接返回一个新的Promise实例,也就是说doSomethingElse()返回的Promise实例并没有返回给then()的响应函数,此时默认返回undefined,并且finalHandler会立刻执行几乎会和doSomethingElse ()同时)

// doSomething
// |------------------|
//                    doSomethingElse(undefined)
//                    |------------------|
//                    finalHandler(undefined)
//                    |------------------|
  1. 问题三
doSomething()
    .then(doSomethingElse())
    .then(finalHandler);

答案:
(第一个then()函数中传入的是一个函数,且采用的是函数执行的方式;实际上传入的是一个Promise实例。这种情况下,doSomethingElsedoSomething的执行可以看作几乎是同时的,因为是在同一个栈中执行的)。

在Promise的规范中,then()中传入的若不是一个函数,这个then()就会被忽略;在这里doSomethingElse执行完返回的是一个Promise。也就是说,finalHandler监听的是doSomething的完成时间,在doSomething完成后,finalHandler就会被执行。

// doSomething
// |------------------|
// doSomethingElse(undefined)
// |----------------------------------|
//                    finalHandler(resultOfDoSomething)
//                    |------------------|
  1. 问题四
doSomething()
    .then(doSomethingElse)
    .then(finalHandler);

答案:
then()接收两个函数作为参数,第一个是fulfilled状态,第二个是rejected状态);在这里doSomethingElse作为fulfilled状态的响应函数对其进行后续处理

// doSomething
// |-----------|
//             doSomethingElse(resultOfDoSomething)
//             |------------|
//                         finalHandler(resultOfDoSomethingElse)
//                         |------------------|

关于错误处理

Promise会自动捕获内部异常(即在执行器当中如果发生错误或自己抛出错误,Promise的状态就会被改为rejected),随后调用rejected响应函数进行处理,也会向后面寻找catch()响应函数进行处理。

  1. catch捕获
console.log('here we go');
new Promise( resolve => {
    setTimeout( () => {
        throw new Error('bye');
    }, 2000);
})
    .then( value => {
        console.log( value + ' world');
    })
    .catch( error => {
        console.log( 'Error:', error.message);
    });

输出结果:(fulfilled响应函数不会得到执行)
here we go
Promise {<pending>}
(约2s后)Uncaught Error: bye
at setTimeout (<anonymous>:4:15)

  1. reject响应捕获
console.log('here we go');
new Promise( (resolve, reject) => {
    setTimeout( () => {
        reject('bye');//throw new Error('bye');
    }, 2000);
})
    .then( value => {
        console.log( value + ' world');
    }, value => {
        console.log( 'Error:', value);
    });

推荐使用第一种,更加清晰好读,并且可以捕获前面所有.then()的错误。

关于catch()

实际上只是 then(null, ...)的语法糖

console.log('here we go');

new Promise(resolve => {
    setTimeout(() => {
        resolve();
    }, 1000);
})
    .then( () => {
        console.log('start');
        throw new Error('test error');
    })
    .catch( err => {
        console.log('I catch:', err);

        // 下面这一行的注释将引发不同的走向
        // throw new Error('another error');
    })
    .then( () => {
        console.log('arrive here');
    })
    .then( () => {
        console.log('... and here');
    })
    .catch( err => {
        console.log('No, I catch:', err);
    });

输出结果:
here we go
Promise {<pending>}
(约1s后)start
I catch: Error: test error
at Promise.then (<anonymous>:10:15)
at <anonymous>
arrive here
and here

可以看出,catch()也返回一个Promise实例,并且catch()内部没有抛出错误时,返回的这个Promise实例是fulfilled状态,所以接下来的then()都会被执行。

注意:

建议在所有队列最后都加上catch(),以避免漏掉错误处理造成意想不到的问题。

References

[翻译] We have a problem with promises

上一篇 下一篇

猜你喜欢

热点阅读